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
;
102 * Return a baseURL for resolving a local-ref URL.
104 * @param aContent an element which uses a local-ref property. Here are some
106 * <rect fill=url(#foo)>
107 * <circle clip-path=url(#foo)>
108 * <use xlink:href="#foo">
110 static already_AddRefed
<nsIURI
> GetBaseURLForLocalRef(nsIContent
* 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
) {
139 nsCOMPtr
<nsIURI
> uri
= aURL
.GetURI();
141 if (aURL
.IsLocalRef()) {
142 uri
= GetBaseURLForLocalRef(aFrame
->GetContent(), uri
);
143 uri
= aURL
.ResolveLocalRef(uri
);
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();
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
);
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.
212 mFramePresShell
= nullptr;
215 // null if the frame has become invalid
217 if (mFramePresShell
&& mFramePresShell
->IsDestroying()) {
218 Detach(); // mFrame is no longer valid.
224 // The frame that our property is attached to (may be null).
226 PresShell
* mFramePresShell
;
229 void SVGRenderingObserver::StartObserving() {
230 Element
* target
= GetReferencedElementWithoutObserving();
232 target
->AddMutationObserver(this);
236 void SVGRenderingObserver::StopObserving() {
237 Element
* target
= GetReferencedElementWithoutObserving();
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() {
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();
270 if (frame
->Type() == aFrameType
) {
280 void SVGRenderingObserver::OnNonDOMMutationRenderingChange() {
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
,
293 const nsAttrValue
* aOldValue
) {
294 if (aElement
->IsInNativeAnonymousSubtree()) {
295 // Don't observe attribute changes in native-anonymous subtrees like
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
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.
316 void SVGRenderingObserver::ContentAppended(nsIContent
* aFirstNewContent
) {
320 void SVGRenderingObserver::ContentInserted(nsIContent
* aChild
) {
324 void SVGRenderingObserver::ContentRemoved(nsIContent
* aChild
,
325 nsIContent
* aPreviousSibling
) {
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
{
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
);
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.
361 void TargetChanged() {
362 mTargetIsValid
= ([this] {
363 Element
* observed
= mObservedElementTracker
.get();
367 // If the content is observing an ancestor, then return the target is not
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
,
377 if (mTargetIsValidCallback
) {
378 return mTargetIsValidCallback(*observed
);
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
{
397 explicit ElementTracker(SVGIDRenderingObserver
* aOwningObserver
)
398 : mOwningObserver(aOwningObserver
) {}
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; }
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
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;
456 uri
= aURI
->GetURI();
457 referrerInfo
= aURI
->GetReferrerInfo();
460 mObservedElementTracker
.ResetToURIFragmentID(
461 aObservingContent
, uri
, referrerInfo
, true, aReferenceImage
);
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(),
476 mInObserverSet
= false;
480 class SVGRenderingObserverProperty
: public SVGIDRenderingObserver
{
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
) {}
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
) {
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
518 nsLayoutUtils::PostRestyleEvent(frame
->GetContent()->AsElement(),
520 nsChangeHint_InvalidateRenderingObservers
);
524 static bool IsSVGGeometryElement(const Element
& aObserved
) {
525 return aObserved
.IsSVGGeometryElement();
528 class SVGTextPathObserver final
: public SVGRenderingObserverProperty
{
530 SVGTextPathObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
531 bool aReferenceImage
)
532 : SVGRenderingObserverProperty(aURI
, aFrame
, aReferenceImage
,
533 kAttributeChanged
, IsSVGGeometryElement
) {}
536 void OnRenderingChange() override
;
539 void SVGTextPathObserver::OnRenderingChange() {
540 SVGRenderingObserverProperty::OnRenderingChange();
542 if (!mTargetIsValid
) {
546 nsIFrame
* frame
= mFrameReference
.Get();
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");
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();
571 text
->ScheduleReflowSVG();
576 class SVGMPathObserver final
: public SVGIDRenderingObserver
{
580 SVGMPathObserver(URLAndReferrerInfo
* aURI
, SVGMPathElement
* aElement
)
581 : SVGIDRenderingObserver(aURI
, aElement
, /* aReferenceImage = */ false,
582 kAttributeChanged
, IsSVGGeometryElement
) {}
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
) {
599 auto* element
= static_cast<SVGMPathElement
*>(mObservingContent
.get());
600 element
->NotifyParentOfMpathChange();
603 class SVGMarkerObserver final
: public SVGRenderingObserverProperty
{
605 SVGMarkerObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
606 bool aReferenceImage
)
607 : SVGRenderingObserverProperty(aURI
, aFrame
, aReferenceImage
,
608 kAttributeChanged
| kContentAppended
|
609 kContentInserted
| kContentRemoved
) {}
612 void OnRenderingChange() override
;
615 void SVGMarkerObserver::OnRenderingChange() {
616 SVGRenderingObserverProperty::OnRenderingChange();
618 nsIFrame
* frame
= mFrameReference
.Get();
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
{
642 SVGPaintingProperty(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
643 bool aReferenceImage
)
644 : SVGRenderingObserverProperty(aURI
, aFrame
, aReferenceImage
) {}
647 void OnRenderingChange() override
;
650 void SVGPaintingProperty::OnRenderingChange() {
651 SVGRenderingObserverProperty::OnRenderingChange();
653 nsIFrame
* frame
= mFrameReference
.Get();
658 if (frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
659 frame
->InvalidateFrameSubtree();
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
{
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
{
699 explicit BackgroundClipRenderingObserver(nsIFrame
* aFrame
) : mFrame(aFrame
) {}
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
719 bool ObservesReflow() final
{ return true; }
721 // The observer and observee!
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
{
752 SVGFilterObserver(URLAndReferrerInfo
* aURI
, nsIContent
* aObservingContent
,
753 SVGFilterObserverList
* aFilterChainObserver
)
754 : SVGIDRenderingObserver(aURI
, aObservingContent
, false,
755 kAttributeChanged
| kContentAppended
|
756 kContentInserted
| kContentRemoved
,
758 mFilterObserverList(aFilterChainObserver
) {}
760 void DetachFromChainObserver() { mFilterObserverList
= nullptr; }
763 * @return the filter frame, or null if there is no filter frame
765 SVGFilterFrame
* GetAndObserveFilterFrame();
768 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
769 NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserver
)
771 // SVGIDRenderingObserver
772 void OnRenderingChange() override
;
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
)
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
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
{
822 SVGFilterObserverList(Span
<const StyleFilter
> aFilters
,
823 nsIContent
* aFilteredElement
,
824 nsIFrame
* aFilteredFrame
= nullptr);
826 const nsTArray
<RefPtr
<SVGFilterObserver
>>& GetObservers() const {
831 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
832 NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList
)
834 virtual void OnRenderingChange() = 0;
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
) {
859 nsIFrame
* frame
= mObservingContent
->GetPrimaryFrame();
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
)
901 SVGFilterObserverList::SVGFilterObserverList(Span
<const StyleFilter
> aFilters
,
902 nsIContent
* aFilteredElement
,
903 nsIFrame
* aFilteredFrame
) {
904 for (const auto& filter
: aFilters
) {
905 if (!filter
.IsUrl()) {
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
);
917 nsCOMPtr
<nsIURI
> resolvedURI
= url
.ResolveLocalRef(aFilteredElement
);
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
{
933 SVGFilterObserverListForCSSProp(Span
<const StyleFilter
> aFilters
,
934 nsIFrame
* aFilteredFrame
)
935 : SVGFilterObserverList(aFilters
, aFilteredFrame
->GetContent(),
939 void OnRenderingChange() override
;
940 bool mInvalidating
= false;
943 void SVGFilterObserverListForCSSProp::OnRenderingChange() {
947 AutoRestore
<bool> guard(mInvalidating
);
948 mInvalidating
= true;
949 for (auto& observer
: mObservers
) {
950 observer
->OnRenderingChange();
954 class SVGFilterObserverListForCanvasContext final
955 : public SVGFilterObserverList
{
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; }
966 CanvasRenderingContext2D
* mContext
;
969 void SVGFilterObserverListForCanvasContext::OnRenderingChange() {
972 "GFX: This should never be called without a context, except during "
973 "cycle collection (when DetachFromContext has been called)");
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
{
988 explicit SVGMaskObserverList(nsIFrame
* aFrame
);
993 const nsTArray
<RefPtr
<SVGPaintingProperty
>>& GetObservers() const {
997 void ResolveImage(uint32_t aIndex
);
1000 virtual ~SVGMaskObserverList() = default; // non-public
1001 nsTArray
<RefPtr
<SVGPaintingProperty
>> mProperties
;
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
;
1015 maskUri
= ResolveURLUsingLocalRef(aFrame
, *data
);
1018 bool hasRef
= false;
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
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()) {
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
{
1060 SVGTemplateElementObserver(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
1061 bool aReferenceImage
)
1062 : SVGIDRenderingObserver(aURI
, aFrame
->GetContent(), aReferenceImage
,
1063 kAttributeChanged
| kContentAppended
|
1064 kContentInserted
| kContentRemoved
),
1065 mFrameReference(aFrame
) {}
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
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
{
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
); }
1122 bool Contains(SVGRenderingObserver
* aObserver
) {
1123 return mObservers
.Contains(aObserver
);
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
1147 nsTHashSet
<SVGRenderingObserver
*> mObservers
;
1150 void SVGRenderingObserverSet::InvalidateAll() {
1151 if (mObservers
.IsEmpty()) {
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()) {
1174 AutoTArray
<SVGRenderingObserver
*, 10> observers
;
1176 for (auto it
= mObservers
.cbegin(), end
= mObservers
.cend(); it
!= end
;
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
));
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 "
1217 MOZ_ASSERT(!mInObserverSet
, "In whose observer set are we, then?");
1222 using URIObserverHashtable
=
1223 nsInterfaceHashtable
<URLAndReferrerInfoHashKey
, nsIMutationObserver
>;
1225 using PaintingPropertyDescriptor
=
1226 const FramePropertyDescriptor
<SVGPaintingProperty
>*;
1228 static void DestroyFilterProperty(SVGFilterObserverListForCSSProp
* aProp
) {
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
)
1257 static T
* GetEffectProperty(URLAndReferrerInfo
* aURI
, nsIFrame
* aFrame
,
1258 const FramePropertyDescriptor
<T
>* aProperty
) {
1264 T
* prop
= aFrame
->GetProperty(aProperty
, &found
);
1266 MOZ_ASSERT(prop
, "this property should only store non-null values");
1269 prop
= new T(aURI
, aFrame
, false);
1271 aFrame
->AddProperty(aProperty
, 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
;
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())
1298 bool foundMarker
= false;
1299 RefPtr
<URLAndReferrerInfo
> markerURL
;
1300 SVGMarkerObserver
* observer
;
1303 #define GET_MARKER(type) \
1304 markerURL = GetMarkerURI(aMarkedFrame, &nsStyleSVG::mMarker##type); \
1306 GetEffectProperty(markerURL, aMarkedFrame, Marker##type##Property()); \
1307 marker = observer ? observer->GetAndObserveReferencedFrame( \
1308 LayoutFrameType::SVGMarker, nullptr) \
1310 foundMarker = foundMarker || bool(marker); \
1311 (*aFrames)[SVGMark::e##type] = static_cast<SVGMarkerFrame*>(marker);
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
) {
1334 SVGFilterObserverListForCSSProp
* observers
=
1335 aFrame
->GetProperty(aProperty
, &found
);
1337 MOZ_ASSERT(observers
, "this property should only store non-null values");
1340 observers
= new SVGFilterObserverListForCSSProp(aFilters
, aFrame
);
1341 NS_ADDREF(observers
);
1342 aFrame
->AddProperty(aProperty
, 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();
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
),
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()) {
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
);
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.
1455 return eHasRefsSomeInvalid
;
1457 if (aClipPathFrame
) {
1458 *aClipPathFrame
= frame
;
1460 return frame
? eHasRefsAllValid
: eHasNoRefs
;
1463 static SVGRenderingObserverProperty
* GetOrCreateGeometryObserver(
1465 // Now only offset-path property uses this. See MotionPathUtils.cpp.
1466 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
1467 if (!disp
->mOffsetPath
.IsUrl()) {
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
);
1481 bool frameTypeOK
= true;
1482 SVGGeometryFrame
* frame
=
1483 do_QueryFrame(observers
->GetAndObserveReferencedFrame(
1484 LayoutFrameType::SVGGeometry
, &frameTypeOK
));
1485 if (!frameTypeOK
|| !frame
) {
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()) {
1502 MOZ_ASSERT(style
->mMask
.mImageCount
> 0);
1505 SVGMaskObserverList
* prop
= aMaskedFrame
->GetProperty(MaskProperty(), &found
);
1507 MOZ_ASSERT(prop
, "this property should only store non-null values");
1510 prop
= new SVGMaskObserverList(aMaskedFrame
);
1512 aMaskedFrame
->AddProperty(MaskProperty(), prop
);
1516 SVGObserverUtils::ReferenceState
SVGObserverUtils::GetAndObserveMasks(
1517 nsIFrame
* aMaskedFrame
, nsTArray
<SVGMaskFrame
*>* aMaskFrames
) {
1518 SVGMaskObserverList
* observerList
= GetOrCreateMaskObserverList(aMaskedFrame
);
1519 if (!observerList
) {
1523 const nsTArray
<RefPtr
<SVGPaintingProperty
>>& observers
=
1524 observerList
->GetObservers();
1525 if (observers
.IsEmpty()) {
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
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
;
1549 aMaskFrames
->AppendElement(maskFrame
);
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());
1566 nsIContent
* content
= aTextPathFrame
->GetContent();
1568 static_cast<SVGTextPathElement
*>(content
)->HrefAsString(href
);
1569 if (href
.IsEmpty()) {
1570 return nullptr; // no URL
1573 RefPtr
<URLAndReferrerInfo
> target
= ResolveURLUsingLocalRef(content
, href
);
1576 GetEffectProperty(target
, aTextPathFrame
, HrefAsTextPathProperty());
1582 return SVGGeometryElement::FromNodeOrNull(
1583 property
->GetAndObserveReferencedElement());
1586 SVGGeometryElement
* SVGObserverUtils::GetAndObserveMPathsPath(
1587 SVGMPathElement
* aSVGMPathElement
) {
1588 if (!aSVGMPathElement
->mMPathObserver
) {
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())
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());
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
) {
1660 URIObserverHashtable
* hashtable
=
1661 aFrame
->GetProperty(BackgroundImageProperty(), &found
);
1663 hashtable
= new URIObserverHashtable();
1664 aFrame
->AddProperty(BackgroundImageProperty(), hashtable
);
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()
1678 ->ReferrerInfoForInternalCSSAndSVGResources();
1679 RefPtr
<URLAndReferrerInfo
> url
=
1680 new URLAndReferrerInfo(targetURI
, referrerInfo
);
1682 return static_cast<SVGMozElementObserver
*>(
1684 ->LookupOrInsertWith(
1687 return MakeRefPtr
<SVGMozElementObserver
>(url
, aFrame
);
1690 ->GetAndObserveReferencedElement();
1693 Element
* SVGObserverUtils::GetAndObserveBackgroundClip(nsIFrame
* aFrame
) {
1695 BackgroundClipRenderingObserver
* obs
=
1696 aFrame
->GetProperty(BackgroundClipObserverProperty(), &found
);
1698 obs
= new BackgroundClipRenderingObserver(aFrame
);
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()) {
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());
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
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
);
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
);
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
);
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());
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();
1830 if (aFrame
->IsRenderingObserverContainer()) {
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();
1844 if (f
->IsRenderingObserverContainer()) {
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
);
1860 if (aFlags
& INVALIDATE_REFLOW
) {
1861 observers
->InvalidateAllForReflow();
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