forked from mirrors/gecko-dev
		
	 a735f4de78
			
		
	
	
		a735f4de78
		
	
	
	
	
		
			
			Scrollbar attribute changes run at uncommon times, and SVG shouldn't really need to care about them (reflowing the relevant nodes should trigger an observer invalidation). Differential Revision: https://phabricator.services.mozilla.com/D121712
		
			
				
	
	
		
			1733 lines
		
	
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1733 lines
		
	
	
	
		
			58 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| // Main header first:
 | |
| #include "SVGObserverUtils.h"
 | |
| 
 | |
| // Keep others in (case-insensitive) order:
 | |
| #include "mozilla/css/ImageLoader.h"
 | |
| #include "mozilla/dom/CanvasRenderingContext2D.h"
 | |
| #include "mozilla/dom/SVGGeometryElement.h"
 | |
| #include "mozilla/dom/SVGTextPathElement.h"
 | |
| #include "mozilla/dom/SVGUseElement.h"
 | |
| #include "mozilla/PresShell.h"
 | |
| #include "mozilla/RestyleManager.h"
 | |
| #include "mozilla/SVGClipPathFrame.h"
 | |
| #include "mozilla/SVGMaskFrame.h"
 | |
| #include "mozilla/SVGTextFrame.h"
 | |
| #include "mozilla/SVGUtils.h"
 | |
| #include "nsCSSFrameConstructor.h"
 | |
| #include "nsCycleCollectionParticipant.h"
 | |
| #include "nsHashKeys.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsIContentInlines.h"
 | |
| #include "nsInterfaceHashtable.h"
 | |
| #include "nsIReflowCallback.h"
 | |
| #include "nsISupportsImpl.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsTHashtable.h"
 | |
| #include "nsURIHashKey.h"
 | |
| #include "SVGFilterFrame.h"
 | |
| #include "SVGMarkerFrame.h"
 | |
| #include "SVGPaintServerFrame.h"
 | |
| 
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| bool URLAndReferrerInfo::operator==(const URLAndReferrerInfo& aRHS) const {
 | |
|   bool uriEqual = false, referrerEqual = false;
 | |
|   this->mURI->Equals(aRHS.mURI, &uriEqual);
 | |
|   this->mReferrerInfo->Equals(aRHS.mReferrerInfo, &referrerEqual);
 | |
| 
 | |
|   return uriEqual && referrerEqual;
 | |
| }
 | |
| 
 | |
| class URLAndReferrerInfoHashKey : public PLDHashEntryHdr {
 | |
|  public:
 | |
|   using KeyType = const URLAndReferrerInfo*;
 | |
|   using KeyTypePointer = const URLAndReferrerInfo*;
 | |
| 
 | |
|   explicit URLAndReferrerInfoHashKey(const URLAndReferrerInfo* aKey) noexcept
 | |
|       : mKey(aKey) {
 | |
|     MOZ_COUNT_CTOR(URLAndReferrerInfoHashKey);
 | |
|   }
 | |
|   URLAndReferrerInfoHashKey(URLAndReferrerInfoHashKey&& aToMove) noexcept
 | |
|       : PLDHashEntryHdr(std::move(aToMove)), mKey(std::move(aToMove.mKey)) {
 | |
|     MOZ_COUNT_CTOR(URLAndReferrerInfoHashKey);
 | |
|   }
 | |
|   MOZ_COUNTED_DTOR(URLAndReferrerInfoHashKey)
 | |
| 
 | |
|   const URLAndReferrerInfo* GetKey() const { return mKey; }
 | |
| 
 | |
|   bool KeyEquals(const URLAndReferrerInfo* aKey) const {
 | |
|     if (!mKey) {
 | |
|       return !aKey;
 | |
|     }
 | |
|     return *mKey == *aKey;
 | |
|   }
 | |
| 
 | |
|   static const URLAndReferrerInfo* KeyToPointer(
 | |
|       const URLAndReferrerInfo* aKey) {
 | |
|     return aKey;
 | |
|   }
 | |
| 
 | |
|   static PLDHashNumber HashKey(const URLAndReferrerInfo* aKey) {
 | |
|     if (!aKey) {
 | |
|       // If the key is null, return hash for empty string.
 | |
|       return HashString(""_ns);
 | |
|     }
 | |
|     nsAutoCString urlSpec, referrerSpec;
 | |
|     // nsURIHashKey ignores GetSpec() failures, so we do too:
 | |
|     Unused << aKey->GetURI()->GetSpec(urlSpec);
 | |
|     return AddToHash(
 | |
|         HashString(urlSpec),
 | |
|         static_cast<ReferrerInfo*>(aKey->GetReferrerInfo())->Hash());
 | |
|   }
 | |
| 
 | |
|   enum { ALLOW_MEMMOVE = true };
 | |
| 
 | |
|  protected:
 | |
|   RefPtr<const URLAndReferrerInfo> mKey;
 | |
| };
 | |
| 
 | |
| static already_AddRefed<URLAndReferrerInfo> ResolveURLUsingLocalRef(
 | |
|     nsIFrame* aFrame, const StyleComputedImageUrl& aURL) {
 | |
|   MOZ_ASSERT(aFrame);
 | |
| 
 | |
|   nsCOMPtr<nsIURI> uri = aURL.GetURI();
 | |
| 
 | |
|   if (aURL.IsLocalRef()) {
 | |
|     uri = SVGObserverUtils::GetBaseURLForLocalRef(aFrame->GetContent(), uri);
 | |
|     uri = aURL.ResolveLocalRef(uri);
 | |
|   }
 | |
| 
 | |
|   if (!uri) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<URLAndReferrerInfo> info =
 | |
|       new URLAndReferrerInfo(uri, aURL.ExtraData());
 | |
|   return info.forget();
 | |
| }
 | |
| 
 | |
| static already_AddRefed<URLAndReferrerInfo> ResolveURLUsingLocalRef(
 | |
|     nsIFrame* aFrame, const nsAString& aURL, nsIReferrerInfo* aReferrerInfo) {
 | |
|   MOZ_ASSERT(aFrame);
 | |
| 
 | |
|   nsIContent* content = aFrame->GetContent();
 | |
| 
 | |
|   // Like SVGObserverUtils::GetBaseURLForLocalRef, we want to resolve the
 | |
|   // URL against any <use> element shadow tree's source document.
 | |
|   //
 | |
|   // Unlike GetBaseURLForLocalRef, we are assuming that the URL was specified
 | |
|   // directly on mFrame's content (because this ResolveURLUsingLocalRef
 | |
|   // overload is used for href="" attributes and not CSS URL values), so there
 | |
|   // is no need to check whether the URL was specified / inherited from
 | |
|   // outside the shadow tree.
 | |
|   nsIURI* base = nullptr;
 | |
|   const Encoding* encoding = nullptr;
 | |
|   if (SVGUseElement* use = content->GetContainingSVGUseShadowHost()) {
 | |
|     base = use->GetSourceDocURI();
 | |
|     encoding = use->GetSourceDocCharacterSet();
 | |
|   }
 | |
| 
 | |
|   if (!base) {
 | |
|     base = content->OwnerDoc()->GetDocumentURI();
 | |
|     encoding = content->OwnerDoc()->GetDocumentCharacterSet();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> uri;
 | |
|   Unused << NS_NewURI(getter_AddRefs(uri), aURL, WrapNotNull(encoding), base);
 | |
| 
 | |
|   if (!uri) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<URLAndReferrerInfo> info = new URLAndReferrerInfo(uri, aReferrerInfo);
 | |
|   return info.forget();
 | |
| }
 | |
| 
 | |
| class SVGFilterObserverList;
 | |
| 
 | |
| /**
 | |
|  * A class used as a member of the "observer" classes below to help them
 | |
|  * avoid dereferencing their frame during presshell teardown when their frame
 | |
|  * may have been destroyed (leaving their pointer to their frame dangling).
 | |
|  *
 | |
|  * When a presshell is torn down, the properties for each frame may not be
 | |
|  * deleted until after the frames are destroyed.  "Observer" objects (attached
 | |
|  * as frame properties) must therefore check whether the presshell is being
 | |
|  * torn down before using their pointer to their frame.
 | |
|  *
 | |
|  * mFramePresShell may be null, but when mFrame is non-null, mFramePresShell
 | |
|  * is guaranteed to be non-null, too.
 | |
|  */
 | |
| struct SVGFrameReferenceFromProperty {
 | |
|   explicit SVGFrameReferenceFromProperty(nsIFrame* aFrame)
 | |
|       : mFrame(aFrame), mFramePresShell(aFrame->PresShell()) {}
 | |
| 
 | |
|   // Clear our reference to the frame.
 | |
|   void Detach() {
 | |
|     mFrame = nullptr;
 | |
|     mFramePresShell = nullptr;
 | |
|   }
 | |
| 
 | |
|   // null if the frame has become invalid
 | |
|   nsIFrame* Get() {
 | |
|     if (mFramePresShell && mFramePresShell->IsDestroying()) {
 | |
|       Detach();  // mFrame is no longer valid.
 | |
|     }
 | |
|     return mFrame;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   // The frame that our property is attached to (may be null).
 | |
|   nsIFrame* mFrame;
 | |
|   PresShell* mFramePresShell;
 | |
| };
 | |
| 
 | |
| void SVGRenderingObserver::StartObserving() {
 | |
|   Element* target = GetReferencedElementWithoutObserving();
 | |
|   if (target) {
 | |
|     target->AddMutationObserver(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::StopObserving() {
 | |
|   Element* target = GetReferencedElementWithoutObserving();
 | |
| 
 | |
|   if (target) {
 | |
|     target->RemoveMutationObserver(this);
 | |
|     if (mInObserverSet) {
 | |
|       SVGObserverUtils::RemoveRenderingObserver(target, this);
 | |
|       mInObserverSet = false;
 | |
|     }
 | |
|   }
 | |
|   NS_ASSERTION(!mInObserverSet, "still in an observer set?");
 | |
| }
 | |
| 
 | |
| Element* SVGRenderingObserver::GetAndObserveReferencedElement() {
 | |
| #ifdef DEBUG
 | |
|   DebugObserverSet();
 | |
| #endif
 | |
|   Element* referencedElement = GetReferencedElementWithoutObserving();
 | |
|   if (referencedElement && !mInObserverSet) {
 | |
|     SVGObserverUtils::AddRenderingObserver(referencedElement, this);
 | |
|     mInObserverSet = true;
 | |
|   }
 | |
|   return referencedElement;
 | |
| }
 | |
| 
 | |
| nsIFrame* SVGRenderingObserver::GetAndObserveReferencedFrame() {
 | |
|   Element* referencedElement = GetAndObserveReferencedElement();
 | |
|   return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr;
 | |
| }
 | |
| 
 | |
| nsIFrame* SVGRenderingObserver::GetAndObserveReferencedFrame(
 | |
|     LayoutFrameType aFrameType, bool* aOK) {
 | |
|   nsIFrame* frame = GetAndObserveReferencedFrame();
 | |
|   if (frame) {
 | |
|     if (frame->Type() == aFrameType) {
 | |
|       return frame;
 | |
|     }
 | |
|     if (aOK) {
 | |
|       *aOK = false;
 | |
|     }
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::OnNonDOMMutationRenderingChange() {
 | |
|   mInObserverSet = false;
 | |
|   OnRenderingChange();
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::NotifyEvictedFromRenderingObserverSet() {
 | |
|   mInObserverSet = false;  // We've been removed from rendering-obs. set.
 | |
|   StopObserving();         // Stop observing mutations too.
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::AttributeChanged(dom::Element* aElement,
 | |
|                                             int32_t aNameSpaceID,
 | |
|                                             nsAtom* aAttribute,
 | |
|                                             int32_t aModType,
 | |
|                                             const nsAttrValue* aOldValue) {
 | |
|   if (aElement->IsInNativeAnonymousSubtree()) {
 | |
|     // Don't observe attribute changes in native-anonymous subtrees like
 | |
|     // scrollbars.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // An attribute belonging to the element that we are observing *or one of its
 | |
|   // descendants* has changed.
 | |
|   //
 | |
|   // In the case of observing a gradient element, say, we want to know if any
 | |
|   // of its 'stop' element children change, but we don't actually want to do
 | |
|   // anything for changes to SMIL element children, for example. Maybe it's not
 | |
|   // worth having logic to optimize for that, but in most cases it could be a
 | |
|   // small check?
 | |
|   //
 | |
|   // XXXjwatt: do we really want to blindly break the link between our
 | |
|   // observers and ourselves for all attribute changes? For non-ID changes
 | |
|   // surely that is unnecessary.
 | |
| 
 | |
|   OnRenderingChange();
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::ContentAppended(nsIContent* aFirstNewContent) {
 | |
|   OnRenderingChange();
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::ContentInserted(nsIContent* aChild) {
 | |
|   OnRenderingChange();
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserver::ContentRemoved(nsIContent* aChild,
 | |
|                                           nsIContent* aPreviousSibling) {
 | |
|   OnRenderingChange();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * SVG elements reference supporting resources by element ID. We need to
 | |
|  * track when those resources change and when the document changes in ways
 | |
|  * that affect which element is referenced by a given ID (e.g., when
 | |
|  * element IDs change). The code here is responsible for that.
 | |
|  *
 | |
|  * When a frame references a supporting resource, we create a property
 | |
|  * object derived from SVGIDRenderingObserver to manage the relationship. The
 | |
|  * property object is attached to the referencing frame.
 | |
|  */
 | |
| class SVGIDRenderingObserver : public SVGRenderingObserver {
 | |
|  public:
 | |
|   SVGIDRenderingObserver(URLAndReferrerInfo* aURI,
 | |
|                          nsIContent* aObservingContent, bool aReferenceImage);
 | |
| 
 | |
|  protected:
 | |
|   virtual ~SVGIDRenderingObserver() {
 | |
|     // This needs to call our GetReferencedElementWithoutObserving override,
 | |
|     // so must be called here rather than in our base class's dtor.
 | |
|     StopObserving();
 | |
|   }
 | |
| 
 | |
|   void TargetChanged() {
 | |
|     mTargetIsValid = ([this] {
 | |
|       Element* observed = mObservedElementTracker.get();
 | |
|       if (!observed) {
 | |
|         return false;
 | |
|       }
 | |
|       // If the content is observing an ancestor, then return the target is not
 | |
|       // valid.
 | |
|       //
 | |
|       // TODO(emilio): Should we allow content observing its own descendants?
 | |
|       // That seems potentially-bad as well.
 | |
|       if (observed->OwnerDoc() == mObservingContent->OwnerDoc() &&
 | |
|           nsContentUtils::ContentIsHostIncludingDescendantOf(mObservingContent,
 | |
|                                                              observed)) {
 | |
|         return false;
 | |
|       }
 | |
|       return true;
 | |
|     }());
 | |
|   }
 | |
| 
 | |
|   Element* GetReferencedElementWithoutObserving() final {
 | |
|     return mTargetIsValid ? mObservedElementTracker.get() : nullptr;
 | |
|   }
 | |
| 
 | |
|   void OnRenderingChange() override;
 | |
| 
 | |
|   /**
 | |
|    * Helper that provides a reference to the element with the ID that our
 | |
|    * observer wants to observe, and that will invalidate our observer if the
 | |
|    * element that that ID identifies changes to a different element (or none).
 | |
|    */
 | |
|   class ElementTracker final : public IDTracker {
 | |
|    public:
 | |
|     explicit ElementTracker(SVGIDRenderingObserver* aOwningObserver)
 | |
|         : mOwningObserver(aOwningObserver) {}
 | |
| 
 | |
|    protected:
 | |
|     void ElementChanged(Element* aFrom, Element* aTo) override {
 | |
|       // Call OnRenderingChange() before the target changes, so that
 | |
|       // mIsTargetValid reflects the right state.
 | |
|       mOwningObserver->OnRenderingChange();
 | |
|       mOwningObserver->StopObserving();
 | |
|       IDTracker::ElementChanged(aFrom, aTo);
 | |
|       mOwningObserver->TargetChanged();
 | |
|       mOwningObserver->StartObserving();
 | |
|       // And same after the target changes, for the same reason.
 | |
|       mOwningObserver->OnRenderingChange();
 | |
|     }
 | |
|     /**
 | |
|      * Override IsPersistent because we want to keep tracking the element
 | |
|      * for the ID even when it changes.
 | |
|      */
 | |
|     bool IsPersistent() override { return true; }
 | |
| 
 | |
|    private:
 | |
|     SVGIDRenderingObserver* mOwningObserver;
 | |
|   };
 | |
| 
 | |
|   ElementTracker mObservedElementTracker;
 | |
|   RefPtr<Element> mObservingContent;
 | |
|   bool mTargetIsValid = false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Note that in the current setup there are two separate observer lists.
 | |
|  *
 | |
|  * In SVGIDRenderingObserver's ctor, the new object adds itself to the
 | |
|  * mutation observer list maintained by the referenced element. In this way the
 | |
|  * SVGIDRenderingObserver is notified if there are any attribute or content
 | |
|  * tree changes to the element or any of its *descendants*.
 | |
|  *
 | |
|  * In SVGIDRenderingObserver::GetAndObserveReferencedElement() the
 | |
|  * SVGIDRenderingObserver object also adds itself to an
 | |
|  * SVGRenderingObserverSet object belonging to the referenced
 | |
|  * element.
 | |
|  *
 | |
|  * XXX: it would be nice to have a clear and concise executive summary of the
 | |
|  * benefits/necessity of maintaining a second observer list.
 | |
|  */
 | |
| SVGIDRenderingObserver::SVGIDRenderingObserver(URLAndReferrerInfo* aURI,
 | |
|                                                nsIContent* aObservingContent,
 | |
|                                                bool aReferenceImage)
 | |
|     : mObservedElementTracker(this),
 | |
|       mObservingContent(aObservingContent->AsElement()) {
 | |
|   // Start watching the target element
 | |
|   nsIURI* uri = nullptr;
 | |
|   nsIReferrerInfo* referrerInfo = nullptr;
 | |
|   if (aURI) {
 | |
|     uri = aURI->GetURI();
 | |
|     referrerInfo = aURI->GetReferrerInfo();
 | |
|   }
 | |
| 
 | |
|   mObservedElementTracker.ResetToURIFragmentID(
 | |
|       aObservingContent, uri, referrerInfo, true, aReferenceImage);
 | |
|   TargetChanged();
 | |
|   StartObserving();
 | |
| }
 | |
| 
 | |
| void SVGIDRenderingObserver::OnRenderingChange() {
 | |
|   if (mObservedElementTracker.get() && mInObserverSet) {
 | |
|     SVGObserverUtils::RemoveRenderingObserver(mObservedElementTracker.get(),
 | |
|                                               this);
 | |
|     mInObserverSet = false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| class SVGRenderingObserverProperty : public SVGIDRenderingObserver {
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   SVGRenderingObserverProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                                bool aReferenceImage)
 | |
|       : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage),
 | |
|         mFrameReference(aFrame) {}
 | |
| 
 | |
|  protected:
 | |
|   virtual ~SVGRenderingObserverProperty() = default;  // non-public
 | |
| 
 | |
|   void OnRenderingChange() override;
 | |
| 
 | |
|   SVGFrameReferenceFromProperty mFrameReference;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(SVGRenderingObserverProperty, nsIMutationObserver)
 | |
| 
 | |
| void SVGRenderingObserverProperty::OnRenderingChange() {
 | |
|   SVGIDRenderingObserver::OnRenderingChange();
 | |
| 
 | |
|   if (!mTargetIsValid) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsIFrame* frame = mFrameReference.Get();
 | |
| 
 | |
|   if (frame && frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) {
 | |
|     // We need to notify anything that is observing the referencing frame or
 | |
|     // any of its ancestors that the referencing frame has been invalidated.
 | |
|     // Since walking the parent chain checking for observers is expensive we
 | |
|     // do that using a change hint (multiple change hints of the same type are
 | |
|     // coalesced).
 | |
|     nsLayoutUtils::PostRestyleEvent(frame->GetContent()->AsElement(),
 | |
|                                     RestyleHint{0},
 | |
|                                     nsChangeHint_InvalidateRenderingObservers);
 | |
|   }
 | |
| }
 | |
| 
 | |
| class SVGTextPathObserver final : public SVGRenderingObserverProperty {
 | |
|  public:
 | |
|   SVGTextPathObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                       bool aReferenceImage)
 | |
|       : SVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
 | |
| 
 | |
|  protected:
 | |
|   void OnRenderingChange() override;
 | |
| };
 | |
| 
 | |
| void SVGTextPathObserver::OnRenderingChange() {
 | |
|   SVGRenderingObserverProperty::OnRenderingChange();
 | |
| 
 | |
|   if (!mTargetIsValid) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsIFrame* frame = mFrameReference.Get();
 | |
|   if (!frame) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(frame->IsFrameOfType(nsIFrame::eSVG) ||
 | |
|                  SVGUtils::IsInSVGTextSubtree(frame),
 | |
|              "SVG frame expected");
 | |
| 
 | |
|   MOZ_ASSERT(frame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
 | |
|              "expected frame for a <textPath> element");
 | |
| 
 | |
|   auto* text = static_cast<SVGTextFrame*>(
 | |
|       nsLayoutUtils::GetClosestFrameOfType(frame, LayoutFrameType::SVGText));
 | |
|   MOZ_ASSERT(text, "expected to find an ancestor SVGTextFrame");
 | |
|   if (text) {
 | |
|     text->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
 | |
|                        NS_STATE_SVG_POSITIONING_DIRTY);
 | |
| 
 | |
|     if (SVGUtils::AnyOuterSVGIsCallingReflowSVG(text)) {
 | |
|       text->AddStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
 | |
|       if (text->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
 | |
|         text->ReflowSVGNonDisplayText();
 | |
|       } else {
 | |
|         text->ReflowSVG();
 | |
|       }
 | |
|     } else {
 | |
|       text->ScheduleReflowSVG();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| class SVGMarkerObserver final : public SVGRenderingObserverProperty {
 | |
|  public:
 | |
|   SVGMarkerObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                     bool aReferenceImage)
 | |
|       : SVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
 | |
| 
 | |
|  protected:
 | |
|   void OnRenderingChange() override;
 | |
| };
 | |
| 
 | |
| void SVGMarkerObserver::OnRenderingChange() {
 | |
|   SVGRenderingObserverProperty::OnRenderingChange();
 | |
| 
 | |
|   nsIFrame* frame = mFrameReference.Get();
 | |
|   if (!frame) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
 | |
| 
 | |
|   // Don't need to request ReflowFrame if we're being reflowed.
 | |
|   if (!frame->HasAnyStateBits(NS_FRAME_IN_REFLOW)) {
 | |
|     // XXXjwatt: We need to unify SVG into standard reflow so we can just use
 | |
|     // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
 | |
|     // XXXSDL KILL THIS!!!
 | |
|     SVGUtils::ScheduleReflowSVG(frame);
 | |
|   }
 | |
|   frame->PresContext()->RestyleManager()->PostRestyleEvent(
 | |
|       frame->GetContent()->AsElement(), RestyleHint{0},
 | |
|       nsChangeHint_RepaintFrame);
 | |
| }
 | |
| 
 | |
| class SVGPaintingProperty : public SVGRenderingObserverProperty {
 | |
|  public:
 | |
|   SVGPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                       bool aReferenceImage)
 | |
|       : SVGRenderingObserverProperty(aURI, aFrame, aReferenceImage) {}
 | |
| 
 | |
|  protected:
 | |
|   void OnRenderingChange() override;
 | |
| };
 | |
| 
 | |
| void SVGPaintingProperty::OnRenderingChange() {
 | |
|   SVGRenderingObserverProperty::OnRenderingChange();
 | |
| 
 | |
|   nsIFrame* frame = mFrameReference.Get();
 | |
|   if (!frame) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
 | |
|     frame->InvalidateFrameSubtree();
 | |
|   } else {
 | |
|     for (nsIFrame* f = frame; f;
 | |
|          f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
 | |
|       f->InvalidateFrame();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| class SVGMozElementObserver final : public SVGPaintingProperty {
 | |
|  public:
 | |
|   SVGMozElementObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                         bool aReferenceImage)
 | |
|       : SVGPaintingProperty(aURI, aFrame, aReferenceImage) {}
 | |
| 
 | |
|   // We only return true here because GetAndObserveBackgroundImage uses us
 | |
|   // to implement observing of arbitrary elements (including HTML elements)
 | |
|   // that may require us to repaint if the referenced element is reflowed.
 | |
|   // Bug 1496065 has been filed to remove that support though.
 | |
|   bool ObservesReflow() override { return true; }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * For content with `background-clip: text`.
 | |
|  *
 | |
|  * This observer is unusual in that the observing frame and observed frame are
 | |
|  * the same frame.  This is because the observing frame is observing for reflow
 | |
|  * of its descendant text nodes, since such reflows may not result in the
 | |
|  * frame's nsDisplayBackground changing.  In other words, Display List Based
 | |
|  * Invalidation may not invalidate the frame's background, so we need this
 | |
|  * observer to make sure that happens.
 | |
|  *
 | |
|  * XXX: It's questionable whether we should even be [ab]using the SVG observer
 | |
|  * mechanism for `background-clip:text`.  Since we know that the observed frame
 | |
|  * is the frame we need to invalidate, we could just check the computed style
 | |
|  * in the (one) place where we pass INVALIDATE_REFLOW and invalidate there...
 | |
|  */
 | |
| class BackgroundClipRenderingObserver : public SVGRenderingObserver {
 | |
|  public:
 | |
|   explicit BackgroundClipRenderingObserver(nsIFrame* aFrame) : mFrame(aFrame) {}
 | |
| 
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|  private:
 | |
|   // We do not call StopObserving() since the observing and observed element
 | |
|   // are the same element (and because we could crash - see bug 1556441).
 | |
|   virtual ~BackgroundClipRenderingObserver() = default;
 | |
| 
 | |
|   Element* GetReferencedElementWithoutObserving() final {
 | |
|     return mFrame->GetContent()->AsElement();
 | |
|   }
 | |
| 
 | |
|   void OnRenderingChange() final;
 | |
| 
 | |
|   /**
 | |
|    * Observing for mutations is not enough.  A new font loading and applying
 | |
|    * to the text content could cause it to reflow, and we need to invalidate
 | |
|    * for that.
 | |
|    */
 | |
|   bool ObservesReflow() final { return true; }
 | |
| 
 | |
|   // The observer and observee!
 | |
|   nsIFrame* mFrame;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(BackgroundClipRenderingObserver, nsIMutationObserver)
 | |
| 
 | |
| void BackgroundClipRenderingObserver::OnRenderingChange() {
 | |
|   for (nsIFrame* f = mFrame; f;
 | |
|        f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
 | |
|     f->InvalidateFrame();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * In a filter chain, there can be multiple SVG reference filters.
 | |
|  * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
 | |
|  *
 | |
|  * This class keeps track of one SVG reference filter in a filter chain.
 | |
|  * e.g. url(#svg-filter-1)
 | |
|  *
 | |
|  * It fires invalidations when the SVG filter element's id changes or when
 | |
|  * the SVG filter element's content changes.
 | |
|  *
 | |
|  * The SVGFilterObserverList class manages a list of SVGFilterObservers.
 | |
|  */
 | |
| class SVGFilterObserver final : public SVGIDRenderingObserver {
 | |
|  public:
 | |
|   SVGFilterObserver(URLAndReferrerInfo* aURI, nsIContent* aObservingContent,
 | |
|                     SVGFilterObserverList* aFilterChainObserver)
 | |
|       : SVGIDRenderingObserver(aURI, aObservingContent, false),
 | |
|         mFilterObserverList(aFilterChainObserver) {}
 | |
| 
 | |
|   void DetachFromChainObserver() { mFilterObserverList = nullptr; }
 | |
| 
 | |
|   /**
 | |
|    * @return the filter frame, or null if there is no filter frame
 | |
|    */
 | |
|   SVGFilterFrame* GetAndObserveFilterFrame();
 | |
| 
 | |
|   // nsISupports
 | |
|   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserver)
 | |
| 
 | |
|   // SVGIDRenderingObserver
 | |
|   void OnRenderingChange() override;
 | |
| 
 | |
|  protected:
 | |
|   virtual ~SVGFilterObserver() = default;  // non-public
 | |
| 
 | |
|   SVGFilterObserverList* mFilterObserverList;
 | |
| };
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserver)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserver)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserver)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserver)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserver)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservedElementTracker)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservingContent)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserver)
 | |
|   tmp->StopObserving();
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservedElementTracker);
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservingContent)
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| SVGFilterFrame* SVGFilterObserver::GetAndObserveFilterFrame() {
 | |
|   return static_cast<SVGFilterFrame*>(
 | |
|       GetAndObserveReferencedFrame(LayoutFrameType::SVGFilter, nullptr));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This class manages a list of SVGFilterObservers, which correspond to
 | |
|  * reference to SVG filters in a list of filters in a given 'filter' property.
 | |
|  * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
 | |
|  *
 | |
|  * In the above example, the SVGFilterObserverList will manage two
 | |
|  * SVGFilterObservers, one for each of the references to SVG filters.  CSS
 | |
|  * filters like "blur(10px)" don't reference filter elements, so they don't
 | |
|  * need an SVGFilterObserver.  The style system invalidates changes to CSS
 | |
|  * filters.
 | |
|  *
 | |
|  * FIXME(emilio): Why do we need this as opposed to the individual observers we
 | |
|  * create in the constructor?
 | |
|  */
 | |
| class SVGFilterObserverList : public nsISupports {
 | |
|  public:
 | |
|   SVGFilterObserverList(Span<const StyleFilter> aFilters,
 | |
|                         nsIContent* aFilteredElement,
 | |
|                         nsIFrame* aFilteredFrame = nullptr);
 | |
| 
 | |
|   const nsTArray<RefPtr<SVGFilterObserver>>& GetObservers() const {
 | |
|     return mObservers;
 | |
|   }
 | |
| 
 | |
|   // nsISupports
 | |
|   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 | |
|   NS_DECL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList)
 | |
| 
 | |
|   virtual void OnRenderingChange() = 0;
 | |
| 
 | |
|  protected:
 | |
|   virtual ~SVGFilterObserverList();
 | |
| 
 | |
|   void DetachObservers() {
 | |
|     for (auto& observer : mObservers) {
 | |
|       observer->DetachFromChainObserver();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsTArray<RefPtr<SVGFilterObserver>> mObservers;
 | |
| };
 | |
| 
 | |
| void SVGFilterObserver::OnRenderingChange() {
 | |
|   SVGIDRenderingObserver::OnRenderingChange();
 | |
| 
 | |
|   if (mFilterObserverList) {
 | |
|     mFilterObserverList->OnRenderingChange();
 | |
|   }
 | |
| 
 | |
|   if (!mTargetIsValid) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsIFrame* frame = mObservingContent->GetPrimaryFrame();
 | |
|   if (!frame) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Repaint asynchronously in case the filter frame is being torn down
 | |
|   nsChangeHint changeHint = nsChangeHint(nsChangeHint_RepaintFrame);
 | |
| 
 | |
|   // Since we don't call SVGRenderingObserverProperty::
 | |
|   // OnRenderingChange, we have to add this bit ourselves.
 | |
|   if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
 | |
|     // Changes should propagate out to things that might be observing
 | |
|     // the referencing frame or its ancestors.
 | |
|     changeHint |= nsChangeHint_InvalidateRenderingObservers;
 | |
|   }
 | |
| 
 | |
|   // Don't need to request UpdateOverflow if we're being reflowed.
 | |
|   if (!frame->HasAnyStateBits(NS_FRAME_IN_REFLOW)) {
 | |
|     changeHint |= nsChangeHint_UpdateOverflow;
 | |
|   }
 | |
|   frame->PresContext()->RestyleManager()->PostRestyleEvent(
 | |
|       mObservingContent, RestyleHint{0}, changeHint);
 | |
| }
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserverList)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserverList)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserverList)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserverList)
 | |
|   tmp->DetachObservers();
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers);
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserverList)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| SVGFilterObserverList::SVGFilterObserverList(Span<const StyleFilter> aFilters,
 | |
|                                              nsIContent* aFilteredElement,
 | |
|                                              nsIFrame* aFilteredFrame) {
 | |
|   for (const auto& filter : aFilters) {
 | |
|     if (!filter.IsUrl()) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     const auto& url = filter.AsUrl();
 | |
| 
 | |
|     // aFilteredFrame can be null if this filter belongs to a
 | |
|     // CanvasRenderingContext2D.
 | |
|     RefPtr<URLAndReferrerInfo> filterURL;
 | |
|     if (aFilteredFrame) {
 | |
|       filterURL = ResolveURLUsingLocalRef(aFilteredFrame, url);
 | |
|     } else {
 | |
|       nsCOMPtr<nsIURI> resolvedURI = url.ResolveLocalRef(aFilteredElement);
 | |
|       if (resolvedURI) {
 | |
|         filterURL = new URLAndReferrerInfo(resolvedURI, url.ExtraData());
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     RefPtr<SVGFilterObserver> observer =
 | |
|         new SVGFilterObserver(filterURL, aFilteredElement, this);
 | |
|     mObservers.AppendElement(observer);
 | |
|   }
 | |
| }
 | |
| 
 | |
| SVGFilterObserverList::~SVGFilterObserverList() { DetachObservers(); }
 | |
| 
 | |
| class SVGFilterObserverListForCSSProp final : public SVGFilterObserverList {
 | |
|  public:
 | |
|   SVGFilterObserverListForCSSProp(Span<const StyleFilter> aFilters,
 | |
|                                   nsIFrame* aFilteredFrame)
 | |
|       : SVGFilterObserverList(aFilters, aFilteredFrame->GetContent(),
 | |
|                               aFilteredFrame) {}
 | |
| 
 | |
|  protected:
 | |
|   void OnRenderingChange() override;
 | |
|   bool mInvalidating = false;
 | |
| };
 | |
| 
 | |
| void SVGFilterObserverListForCSSProp::OnRenderingChange() {
 | |
|   if (mInvalidating) {
 | |
|     return;
 | |
|   }
 | |
|   AutoRestore<bool> guard(mInvalidating);
 | |
|   mInvalidating = true;
 | |
|   for (auto& observer : mObservers) {
 | |
|     observer->OnRenderingChange();
 | |
|   }
 | |
| }
 | |
| 
 | |
| class SVGFilterObserverListForCanvasContext final
 | |
|     : public SVGFilterObserverList {
 | |
|  public:
 | |
|   SVGFilterObserverListForCanvasContext(CanvasRenderingContext2D* aContext,
 | |
|                                         Element* aCanvasElement,
 | |
|                                         Span<const StyleFilter> aFilters)
 | |
|       : SVGFilterObserverList(aFilters, aCanvasElement), mContext(aContext) {}
 | |
| 
 | |
|   void OnRenderingChange() override;
 | |
|   void DetachFromContext() { mContext = nullptr; }
 | |
| 
 | |
|  private:
 | |
|   CanvasRenderingContext2D* mContext;
 | |
| };
 | |
| 
 | |
| void SVGFilterObserverListForCanvasContext::OnRenderingChange() {
 | |
|   if (!mContext) {
 | |
|     MOZ_CRASH("GFX: This should never be called without a context");
 | |
|   }
 | |
|   // Refresh the cached FilterDescription in mContext->CurrentState().filter.
 | |
|   // If this filter is not at the top of the state stack, we'll refresh the
 | |
|   // wrong filter, but that's ok, because we'll refresh the right filter
 | |
|   // when we pop the state stack in CanvasRenderingContext2D::Restore().
 | |
|   RefPtr<CanvasRenderingContext2D> kungFuDeathGrip(mContext);
 | |
|   kungFuDeathGrip->UpdateFilter();
 | |
| }
 | |
| 
 | |
| class SVGMaskObserverList final : public nsISupports {
 | |
|  public:
 | |
|   explicit SVGMaskObserverList(nsIFrame* aFrame);
 | |
| 
 | |
|   // nsISupports
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   const nsTArray<RefPtr<SVGPaintingProperty>>& GetObservers() const {
 | |
|     return mProperties;
 | |
|   }
 | |
| 
 | |
|   void ResolveImage(uint32_t aIndex);
 | |
| 
 | |
|  private:
 | |
|   virtual ~SVGMaskObserverList() = default;  // non-public
 | |
|   nsTArray<RefPtr<SVGPaintingProperty>> mProperties;
 | |
|   nsIFrame* mFrame;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(SVGMaskObserverList, nsISupports)
 | |
| 
 | |
| SVGMaskObserverList::SVGMaskObserverList(nsIFrame* aFrame) : mFrame(aFrame) {
 | |
|   const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
 | |
| 
 | |
|   for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
 | |
|     const StyleComputedImageUrl* data =
 | |
|         svgReset->mMask.mLayers[i].mImage.GetImageRequestURLValue();
 | |
|     RefPtr<URLAndReferrerInfo> maskUri;
 | |
|     if (data) {
 | |
|       maskUri = ResolveURLUsingLocalRef(aFrame, *data);
 | |
|     }
 | |
| 
 | |
|     bool hasRef = false;
 | |
|     if (maskUri) {
 | |
|       maskUri->GetURI()->GetHasRef(&hasRef);
 | |
|     }
 | |
| 
 | |
|     // Accrording to maskUri, SVGPaintingProperty's ctor may trigger an
 | |
|     // external SVG resource download, so we should pass maskUri in only if
 | |
|     // maskUri has a chance pointing to an SVG mask resource.
 | |
|     //
 | |
|     // And, an URL may refer to an SVG mask resource if it consists of
 | |
|     // a fragment.
 | |
|     SVGPaintingProperty* prop = new SVGPaintingProperty(
 | |
|         hasRef ? maskUri.get() : nullptr, aFrame, false);
 | |
|     mProperties.AppendElement(prop);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGMaskObserverList::ResolveImage(uint32_t aIndex) {
 | |
|   const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
 | |
|   MOZ_ASSERT(aIndex < svgReset->mMask.mImageCount);
 | |
| 
 | |
|   auto& image = const_cast<StyleImage&>(svgReset->mMask.mLayers[aIndex].mImage);
 | |
|   if (image.IsResolved()) {
 | |
|     return;
 | |
|   }
 | |
|   MOZ_ASSERT(image.IsImageRequestType());
 | |
|   Document* doc = mFrame->PresContext()->Document();
 | |
|   image.ResolveImage(*doc, nullptr);
 | |
|   if (imgRequestProxy* req = image.GetImageRequest()) {
 | |
|     // FIXME(emilio): What disassociates this request?
 | |
|     doc->StyleImageLoader()->AssociateRequestToFrame(req, mFrame);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Used for gradient-to-gradient, pattern-to-pattern and filter-to-filter
 | |
|  * references to "template" elements (specified via the 'href' attributes).
 | |
|  *
 | |
|  * This is a special class for the case where we know we only want to call
 | |
|  * InvalidateDirectRenderingObservers (as opposed to
 | |
|  * InvalidateRenderingObservers).
 | |
|  *
 | |
|  * TODO(jwatt): If we added a new NS_FRAME_RENDERING_OBSERVER_CONTAINER state
 | |
|  * bit to clipPath, filter, gradients, marker, mask, pattern and symbol, and
 | |
|  * could have InvalidateRenderingObservers stop on reaching such an element,
 | |
|  * then we would no longer need this class (not to mention improving perf by
 | |
|  * significantly cutting down on ancestor traversal).
 | |
|  */
 | |
| class SVGTemplateElementObserver : public SVGIDRenderingObserver {
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   SVGTemplateElementObserver(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                              bool aReferenceImage)
 | |
|       : SVGIDRenderingObserver(aURI, aFrame->GetContent(), aReferenceImage),
 | |
|         mFrameReference(aFrame) {}
 | |
| 
 | |
|  protected:
 | |
|   virtual ~SVGTemplateElementObserver() = default;  // non-public
 | |
| 
 | |
|   void OnRenderingChange() override;
 | |
| 
 | |
|   SVGFrameReferenceFromProperty mFrameReference;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(SVGTemplateElementObserver, nsIMutationObserver)
 | |
| 
 | |
| void SVGTemplateElementObserver::OnRenderingChange() {
 | |
|   SVGIDRenderingObserver::OnRenderingChange();
 | |
| 
 | |
|   if (nsIFrame* frame = mFrameReference.Get()) {
 | |
|     // We know that we don't need to walk the parent chain notifying rendering
 | |
|     // observers since changes to a gradient etc. do not affect ancestor
 | |
|     // elements.  So we only invalidate *direct* rendering observers here.
 | |
|     // Since we don't need to walk the parent chain, we don't need to worry
 | |
|     // about coalescing multiple invalidations by using a change hint as we do
 | |
|     // in SVGRenderingObserverProperty::OnRenderingChange.
 | |
|     SVGObserverUtils::InvalidateDirectRenderingObservers(frame);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * An instance of this class is stored on an observed frame (as a frame
 | |
|  * property) whenever the frame has active rendering observers.  It is used to
 | |
|  * store pointers to the SVGRenderingObserver instances belonging to any
 | |
|  * observing frames, allowing invalidations from the observed frame to be sent
 | |
|  * to all observing frames.
 | |
|  *
 | |
|  * SVGRenderingObserver instances that are added are not strongly referenced,
 | |
|  * so they must remove themselves before they die.
 | |
|  *
 | |
|  * This class is "single-shot", which is to say that when something about the
 | |
|  * observed element changes, InvalidateAll() clears our hashtable of
 | |
|  * SVGRenderingObservers.  SVGRenderingObserver objects will be added back
 | |
|  * again if/when the observing frame looks up our observed frame to use it.
 | |
|  *
 | |
|  * XXXjwatt: is this the best thing to do nowadays?  Back when that mechanism
 | |
|  * landed in bug 330498 we had two pass, recursive invalidation up the frame
 | |
|  * tree, and I think reference loops were a problem.  Nowadays maybe a flag
 | |
|  * on the SVGRenderingObserver objects to coalesce invalidations may work
 | |
|  * better?
 | |
|  *
 | |
|  * InvalidateAll must be called before this object is destroyed, i.e.
 | |
|  * before the referenced frame is destroyed. This should normally happen
 | |
|  * via SVGContainerFrame::RemoveFrame, since only frames in the frame
 | |
|  * tree should be referenced.
 | |
|  */
 | |
| class SVGRenderingObserverSet {
 | |
|  public:
 | |
|   SVGRenderingObserverSet() : mObservers(4) {
 | |
|     MOZ_COUNT_CTOR(SVGRenderingObserverSet);
 | |
|   }
 | |
| 
 | |
|   ~SVGRenderingObserverSet() {
 | |
|     InvalidateAll();
 | |
|     MOZ_COUNT_DTOR(SVGRenderingObserverSet);
 | |
|   }
 | |
| 
 | |
|   void Add(SVGRenderingObserver* aObserver) { mObservers.Insert(aObserver); }
 | |
|   void Remove(SVGRenderingObserver* aObserver) { mObservers.Remove(aObserver); }
 | |
| #ifdef DEBUG
 | |
|   bool Contains(SVGRenderingObserver* aObserver) {
 | |
|     return mObservers.Contains(aObserver);
 | |
|   }
 | |
| #endif
 | |
|   bool IsEmpty() { return mObservers.IsEmpty(); }
 | |
| 
 | |
|   /**
 | |
|    * Drop all our observers, and notify them that we have changed and dropped
 | |
|    * our reference to them.
 | |
|    */
 | |
|   void InvalidateAll();
 | |
| 
 | |
|   /**
 | |
|    * Drop all observers that observe reflow, and notify them that we have
 | |
|    * changed and dropped our reference to them.
 | |
|    */
 | |
|   void InvalidateAllForReflow();
 | |
| 
 | |
|   /**
 | |
|    * Drop all our observers, and notify them that we have dropped our reference
 | |
|    * to them.
 | |
|    */
 | |
|   void RemoveAll();
 | |
| 
 | |
|  private:
 | |
|   nsTHashSet<SVGRenderingObserver*> mObservers;
 | |
| };
 | |
| 
 | |
| void SVGRenderingObserverSet::InvalidateAll() {
 | |
|   if (mObservers.IsEmpty()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const auto observers = std::move(mObservers);
 | |
| 
 | |
|   for (const auto& observer : observers) {
 | |
|     observer->OnNonDOMMutationRenderingChange();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserverSet::InvalidateAllForReflow() {
 | |
|   if (mObservers.IsEmpty()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   AutoTArray<SVGRenderingObserver*, 10> observers;
 | |
| 
 | |
|   for (auto it = mObservers.cbegin(), end = mObservers.cend(); it != end;
 | |
|        ++it) {
 | |
|     SVGRenderingObserver* obs = *it;
 | |
|     if (obs->ObservesReflow()) {
 | |
|       observers.AppendElement(obs);
 | |
|       mObservers.Remove(it);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < observers.Length(); ++i) {
 | |
|     observers[i]->OnNonDOMMutationRenderingChange();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGRenderingObserverSet::RemoveAll() {
 | |
|   const auto observers = std::move(mObservers);
 | |
| 
 | |
|   // Our list is now cleared.  We need to notify the observers we've removed,
 | |
|   // so they can update their state & remove themselves as mutation-observers.
 | |
|   for (const auto& observer : observers) {
 | |
|     observer->NotifyEvictedFromRenderingObserverSet();
 | |
|   }
 | |
| }
 | |
| 
 | |
| static SVGRenderingObserverSet* GetObserverSet(Element* aElement) {
 | |
|   return static_cast<SVGRenderingObserverSet*>(
 | |
|       aElement->GetProperty(nsGkAtoms::renderingobserverset));
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| // Defined down here because we need SVGRenderingObserverSet's definition.
 | |
| void SVGRenderingObserver::DebugObserverSet() {
 | |
|   Element* referencedElement = GetReferencedElementWithoutObserving();
 | |
|   if (referencedElement) {
 | |
|     SVGRenderingObserverSet* observers = GetObserverSet(referencedElement);
 | |
|     bool inObserverSet = observers && observers->Contains(this);
 | |
|     MOZ_ASSERT(inObserverSet == mInObserverSet,
 | |
|                "failed to track whether we're in our referenced element's "
 | |
|                "observer set!");
 | |
|   } else {
 | |
|     MOZ_ASSERT(!mInObserverSet, "In whose observer set are we, then?");
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| using URIObserverHashtable =
 | |
|     nsInterfaceHashtable<URLAndReferrerInfoHashKey, nsIMutationObserver>;
 | |
| 
 | |
| using PaintingPropertyDescriptor =
 | |
|     const FramePropertyDescriptor<SVGPaintingProperty>*;
 | |
| 
 | |
| static void DestroyFilterProperty(SVGFilterObserverListForCSSProp* aProp) {
 | |
|   aProp->Release();
 | |
| }
 | |
| 
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefToTemplateProperty,
 | |
|                                      SVGTemplateElementObserver)
 | |
| NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(FilterProperty,
 | |
|                                     SVGFilterObserverListForCSSProp,
 | |
|                                     DestroyFilterProperty)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MaskProperty, SVGMaskObserverList)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(ClipPathProperty, SVGPaintingProperty)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerStartProperty, SVGMarkerObserver)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerMidProperty, SVGMarkerObserver)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(MarkerEndProperty, SVGMarkerObserver)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(FillProperty, SVGPaintingProperty)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(StrokeProperty, SVGPaintingProperty)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(HrefAsTextPathProperty,
 | |
|                                      SVGTextPathObserver)
 | |
| NS_DECLARE_FRAME_PROPERTY_DELETABLE(BackgroundImageProperty,
 | |
|                                     URIObserverHashtable)
 | |
| NS_DECLARE_FRAME_PROPERTY_RELEASABLE(BackgroundClipObserverProperty,
 | |
|                                      BackgroundClipRenderingObserver)
 | |
| 
 | |
| template <class T>
 | |
| static T* GetEffectProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|                             const FramePropertyDescriptor<T>* aProperty) {
 | |
|   if (!aURI) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   bool found;
 | |
|   T* prop = aFrame->GetProperty(aProperty, &found);
 | |
|   if (found) {
 | |
|     MOZ_ASSERT(prop, "this property should only store non-null values");
 | |
|     return prop;
 | |
|   }
 | |
|   prop = new T(aURI, aFrame, false);
 | |
|   NS_ADDREF(prop);
 | |
|   aFrame->AddProperty(aProperty, prop);
 | |
|   return prop;
 | |
| }
 | |
| 
 | |
| static SVGPaintingProperty* GetPaintingProperty(
 | |
|     URLAndReferrerInfo* aURI, nsIFrame* aFrame,
 | |
|     const FramePropertyDescriptor<SVGPaintingProperty>* aProperty) {
 | |
|   return GetEffectProperty(aURI, aFrame, aProperty);
 | |
| }
 | |
| 
 | |
| static already_AddRefed<URLAndReferrerInfo> GetMarkerURI(
 | |
|     nsIFrame* aFrame, const StyleUrlOrNone nsStyleSVG::*aMarker) {
 | |
|   const StyleUrlOrNone& url = aFrame->StyleSVG()->*aMarker;
 | |
|   if (url.IsNone()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return ResolveURLUsingLocalRef(aFrame, url.AsUrl());
 | |
| }
 | |
| 
 | |
| bool SVGObserverUtils::GetAndObserveMarkers(nsIFrame* aMarkedFrame,
 | |
|                                             SVGMarkerFrame* (*aFrames)[3]) {
 | |
|   MOZ_ASSERT(!aMarkedFrame->GetPrevContinuation() &&
 | |
|                  aMarkedFrame->IsSVGGeometryFrame() &&
 | |
|                  static_cast<SVGGeometryElement*>(aMarkedFrame->GetContent())
 | |
|                      ->IsMarkable(),
 | |
|              "Bad frame");
 | |
| 
 | |
|   bool foundMarker = false;
 | |
|   RefPtr<URLAndReferrerInfo> markerURL;
 | |
|   SVGMarkerObserver* observer;
 | |
|   nsIFrame* marker;
 | |
| 
 | |
| #define GET_MARKER(type)                                                    \
 | |
|   markerURL = GetMarkerURI(aMarkedFrame, &nsStyleSVG::mMarker##type);       \
 | |
|   observer =                                                                \
 | |
|       GetEffectProperty(markerURL, aMarkedFrame, Marker##type##Property()); \
 | |
|   marker = observer ? observer->GetAndObserveReferencedFrame(               \
 | |
|                           LayoutFrameType::SVGMarker, nullptr)              \
 | |
|                     : nullptr;                                              \
 | |
|   foundMarker = foundMarker || bool(marker);                                \
 | |
|   (*aFrames)[SVGMark::e##type] = static_cast<SVGMarkerFrame*>(marker);
 | |
| 
 | |
|   GET_MARKER(Start)
 | |
|   GET_MARKER(Mid)
 | |
|   GET_MARKER(End)
 | |
| 
 | |
| #undef GET_MARKER
 | |
| 
 | |
|   return foundMarker;
 | |
| }
 | |
| 
 | |
| // Note that the returned list will be empty in the case of a 'filter' property
 | |
| // that only specifies CSS filter functions (no url()'s to SVG filters).
 | |
| static SVGFilterObserverListForCSSProp* GetOrCreateFilterObserverListForCSS(
 | |
|     nsIFrame* aFrame) {
 | |
|   MOZ_ASSERT(!aFrame->GetPrevContinuation(), "Require first continuation");
 | |
| 
 | |
|   const nsStyleEffects* effects = aFrame->StyleEffects();
 | |
|   if (!effects->HasFilters()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   bool found;
 | |
|   SVGFilterObserverListForCSSProp* observers =
 | |
|       aFrame->GetProperty(FilterProperty(), &found);
 | |
|   if (found) {
 | |
|     MOZ_ASSERT(observers, "this property should only store non-null values");
 | |
|     return observers;
 | |
|   }
 | |
|   observers =
 | |
|       new SVGFilterObserverListForCSSProp(effects->mFilters.AsSpan(), aFrame);
 | |
|   NS_ADDREF(observers);
 | |
|   aFrame->AddProperty(FilterProperty(), observers);
 | |
|   return observers;
 | |
| }
 | |
| 
 | |
| static SVGObserverUtils::ReferenceState GetAndObserveFilters(
 | |
|     SVGFilterObserverListForCSSProp* aObserverList,
 | |
|     nsTArray<SVGFilterFrame*>* aFilterFrames) {
 | |
|   if (!aObserverList) {
 | |
|     return SVGObserverUtils::eHasNoRefs;
 | |
|   }
 | |
| 
 | |
|   const nsTArray<RefPtr<SVGFilterObserver>>& observers =
 | |
|       aObserverList->GetObservers();
 | |
|   if (observers.IsEmpty()) {
 | |
|     return SVGObserverUtils::eHasNoRefs;
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < observers.Length(); i++) {
 | |
|     SVGFilterFrame* filter = observers[i]->GetAndObserveFilterFrame();
 | |
|     if (!filter) {
 | |
|       if (aFilterFrames) {
 | |
|         aFilterFrames->Clear();
 | |
|       }
 | |
|       return SVGObserverUtils::eHasRefsSomeInvalid;
 | |
|     }
 | |
|     if (aFilterFrames) {
 | |
|       aFilterFrames->AppendElement(filter);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return SVGObserverUtils::eHasRefsAllValid;
 | |
| }
 | |
| 
 | |
| SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveFilters(
 | |
|     nsIFrame* aFilteredFrame, nsTArray<SVGFilterFrame*>* aFilterFrames) {
 | |
|   SVGFilterObserverListForCSSProp* observerList =
 | |
|       GetOrCreateFilterObserverListForCSS(aFilteredFrame);
 | |
|   return mozilla::GetAndObserveFilters(observerList, aFilterFrames);
 | |
| }
 | |
| 
 | |
| SVGObserverUtils::ReferenceState SVGObserverUtils::GetFiltersIfObserving(
 | |
|     nsIFrame* aFilteredFrame, nsTArray<SVGFilterFrame*>* aFilterFrames) {
 | |
|   SVGFilterObserverListForCSSProp* observerList =
 | |
|       aFilteredFrame->GetProperty(FilterProperty());
 | |
|   return mozilla::GetAndObserveFilters(observerList, aFilterFrames);
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsISupports> SVGObserverUtils::ObserveFiltersForCanvasContext(
 | |
|     CanvasRenderingContext2D* aContext, Element* aCanvasElement,
 | |
|     const Span<const StyleFilter> aFilters) {
 | |
|   return do_AddRef(new SVGFilterObserverListForCanvasContext(
 | |
|       aContext, aCanvasElement, aFilters));
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::DetachFromCanvasContext(nsISupports* aAutoObserver) {
 | |
|   static_cast<SVGFilterObserverListForCanvasContext*>(aAutoObserver)
 | |
|       ->DetachFromContext();
 | |
| }
 | |
| 
 | |
| static SVGPaintingProperty* GetOrCreateClipPathObserver(
 | |
|     nsIFrame* aClippedFrame) {
 | |
|   MOZ_ASSERT(!aClippedFrame->GetPrevContinuation(),
 | |
|              "Require first continuation");
 | |
| 
 | |
|   const nsStyleSVGReset* svgStyleReset = aClippedFrame->StyleSVGReset();
 | |
|   if (!svgStyleReset->mClipPath.IsUrl()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   const auto& url = svgStyleReset->mClipPath.AsUrl();
 | |
|   RefPtr<URLAndReferrerInfo> pathURI =
 | |
|       ResolveURLUsingLocalRef(aClippedFrame, url);
 | |
|   return GetPaintingProperty(pathURI, aClippedFrame, ClipPathProperty());
 | |
| }
 | |
| 
 | |
| SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveClipPath(
 | |
|     nsIFrame* aClippedFrame, SVGClipPathFrame** aClipPathFrame) {
 | |
|   if (aClipPathFrame) {
 | |
|     *aClipPathFrame = nullptr;
 | |
|   }
 | |
|   SVGPaintingProperty* observers = GetOrCreateClipPathObserver(aClippedFrame);
 | |
|   if (!observers) {
 | |
|     return eHasNoRefs;
 | |
|   }
 | |
|   bool frameTypeOK = true;
 | |
|   SVGClipPathFrame* frame =
 | |
|       static_cast<SVGClipPathFrame*>(observers->GetAndObserveReferencedFrame(
 | |
|           LayoutFrameType::SVGClipPath, &frameTypeOK));
 | |
|   // Note that, unlike for filters, a reference to an ID that doesn't exist
 | |
|   // is not invalid for clip-path or mask.
 | |
|   if (!frameTypeOK || (frame && !frame->IsValid())) {
 | |
|     return eHasRefsSomeInvalid;
 | |
|   }
 | |
|   if (aClipPathFrame) {
 | |
|     *aClipPathFrame = frame;
 | |
|   }
 | |
|   return frame ? eHasRefsAllValid : eHasNoRefs;
 | |
| }
 | |
| 
 | |
| static SVGMaskObserverList* GetOrCreateMaskObserverList(
 | |
|     nsIFrame* aMaskedFrame) {
 | |
|   MOZ_ASSERT(!aMaskedFrame->GetPrevContinuation(),
 | |
|              "Require first continuation");
 | |
| 
 | |
|   const nsStyleSVGReset* style = aMaskedFrame->StyleSVGReset();
 | |
|   if (!style->HasMask()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(style->mMask.mImageCount > 0);
 | |
| 
 | |
|   bool found;
 | |
|   SVGMaskObserverList* prop = aMaskedFrame->GetProperty(MaskProperty(), &found);
 | |
|   if (found) {
 | |
|     MOZ_ASSERT(prop, "this property should only store non-null values");
 | |
|     return prop;
 | |
|   }
 | |
|   prop = new SVGMaskObserverList(aMaskedFrame);
 | |
|   NS_ADDREF(prop);
 | |
|   aMaskedFrame->AddProperty(MaskProperty(), prop);
 | |
|   return prop;
 | |
| }
 | |
| 
 | |
| SVGObserverUtils::ReferenceState SVGObserverUtils::GetAndObserveMasks(
 | |
|     nsIFrame* aMaskedFrame, nsTArray<SVGMaskFrame*>* aMaskFrames) {
 | |
|   SVGMaskObserverList* observerList = GetOrCreateMaskObserverList(aMaskedFrame);
 | |
|   if (!observerList) {
 | |
|     return eHasNoRefs;
 | |
|   }
 | |
| 
 | |
|   const nsTArray<RefPtr<SVGPaintingProperty>>& observers =
 | |
|       observerList->GetObservers();
 | |
|   if (observers.IsEmpty()) {
 | |
|     return eHasNoRefs;
 | |
|   }
 | |
| 
 | |
|   ReferenceState state = eHasRefsAllValid;
 | |
| 
 | |
|   for (size_t i = 0; i < observers.Length(); i++) {
 | |
|     bool frameTypeOK = true;
 | |
|     SVGMaskFrame* maskFrame =
 | |
|         static_cast<SVGMaskFrame*>(observers[i]->GetAndObserveReferencedFrame(
 | |
|             LayoutFrameType::SVGMask, &frameTypeOK));
 | |
|     MOZ_ASSERT(!maskFrame || frameTypeOK);
 | |
|     // XXXjwatt: this looks fishy
 | |
|     if (!frameTypeOK) {
 | |
|       // We can not find the specific SVG mask resource in the downloaded SVG
 | |
|       // document. There are two possibilities:
 | |
|       // 1. The given resource id is invalid.
 | |
|       // 2. The given resource id refers to a viewbox.
 | |
|       //
 | |
|       // Hand it over to the style image.
 | |
|       observerList->ResolveImage(i);
 | |
|       state = eHasRefsSomeInvalid;
 | |
|     }
 | |
|     if (aMaskFrames) {
 | |
|       aMaskFrames->AppendElement(maskFrame);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return state;
 | |
| }
 | |
| 
 | |
| SVGGeometryElement* SVGObserverUtils::GetAndObserveTextPathsPath(
 | |
|     nsIFrame* aTextPathFrame) {
 | |
|   SVGTextPathObserver* property =
 | |
|       aTextPathFrame->GetProperty(HrefAsTextPathProperty());
 | |
| 
 | |
|   if (!property) {
 | |
|     nsIContent* content = aTextPathFrame->GetContent();
 | |
|     nsAutoString href;
 | |
|     static_cast<SVGTextPathElement*>(content)->HrefAsString(href);
 | |
|     if (href.IsEmpty()) {
 | |
|       return nullptr;  // no URL
 | |
|     }
 | |
| 
 | |
|     // There's no clear refererer policy spec about non-CSS SVG resource
 | |
|     // references Bug 1415044 to investigate which referrer we should use
 | |
|     nsCOMPtr<nsIReferrerInfo> referrerInfo =
 | |
|         ReferrerInfo::CreateForSVGResources(content->OwnerDoc());
 | |
|     RefPtr<URLAndReferrerInfo> target =
 | |
|         ResolveURLUsingLocalRef(aTextPathFrame, href, referrerInfo);
 | |
| 
 | |
|     property =
 | |
|         GetEffectProperty(target, aTextPathFrame, HrefAsTextPathProperty());
 | |
|     if (!property) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Element* element = property->GetAndObserveReferencedElement();
 | |
|   return (element && element->IsNodeOfType(nsINode::eSHAPE))
 | |
|              ? static_cast<SVGGeometryElement*>(element)
 | |
|              : nullptr;
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::InitiateResourceDocLoads(nsIFrame* aFrame) {
 | |
|   // We create observer objects and attach them to aFrame, but we do not
 | |
|   // make aFrame start observing the referenced frames.
 | |
|   Unused << GetOrCreateFilterObserverListForCSS(aFrame);
 | |
|   Unused << GetOrCreateClipPathObserver(aFrame);
 | |
|   Unused << GetOrCreateMaskObserverList(aFrame);
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::RemoveTextPathObserver(nsIFrame* aTextPathFrame) {
 | |
|   aTextPathFrame->RemoveProperty(HrefAsTextPathProperty());
 | |
| }
 | |
| 
 | |
| nsIFrame* SVGObserverUtils::GetAndObserveTemplate(
 | |
|     nsIFrame* aFrame, HrefToTemplateCallback aGetHref) {
 | |
|   SVGTemplateElementObserver* observer =
 | |
|       aFrame->GetProperty(HrefToTemplateProperty());
 | |
| 
 | |
|   if (!observer) {
 | |
|     nsAutoString href;
 | |
|     aGetHref(href);
 | |
|     if (href.IsEmpty()) {
 | |
|       return nullptr;  // no URL
 | |
|     }
 | |
| 
 | |
|     // Convert href to an nsIURI
 | |
|     nsIContent* content = aFrame->GetContent();
 | |
|     nsCOMPtr<nsIURI> targetURI;
 | |
|     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
 | |
|                                               content->GetUncomposedDoc(),
 | |
|                                               content->GetBaseURI());
 | |
| 
 | |
|     // There's no clear refererer policy spec about non-CSS SVG resource
 | |
|     // references.  Bug 1415044 to investigate which referrer we should use.
 | |
|     nsCOMPtr<nsIReferrerInfo> referrerInfo =
 | |
|         ReferrerInfo::CreateForSVGResources(content->OwnerDoc());
 | |
|     RefPtr<URLAndReferrerInfo> target =
 | |
|         new URLAndReferrerInfo(targetURI, referrerInfo);
 | |
| 
 | |
|     observer = GetEffectProperty(target, aFrame, HrefToTemplateProperty());
 | |
|   }
 | |
| 
 | |
|   return observer ? observer->GetAndObserveReferencedFrame() : nullptr;
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::RemoveTemplateObserver(nsIFrame* aFrame) {
 | |
|   aFrame->RemoveProperty(HrefToTemplateProperty());
 | |
| }
 | |
| 
 | |
| Element* SVGObserverUtils::GetAndObserveBackgroundImage(nsIFrame* aFrame,
 | |
|                                                         const nsAtom* aHref) {
 | |
|   bool found;
 | |
|   URIObserverHashtable* hashtable =
 | |
|       aFrame->GetProperty(BackgroundImageProperty(), &found);
 | |
|   if (!found) {
 | |
|     hashtable = new URIObserverHashtable();
 | |
|     aFrame->AddProperty(BackgroundImageProperty(), hashtable);
 | |
|   } else {
 | |
|     MOZ_ASSERT(hashtable, "this property should only store non-null values");
 | |
|   }
 | |
| 
 | |
|   nsAutoString elementId = u"#"_ns + nsDependentAtomString(aHref);
 | |
|   nsCOMPtr<nsIURI> targetURI;
 | |
|   nsContentUtils::NewURIWithDocumentCharset(
 | |
|       getter_AddRefs(targetURI), elementId,
 | |
|       aFrame->GetContent()->GetUncomposedDoc(),
 | |
|       aFrame->GetContent()->GetBaseURI());
 | |
|   nsCOMPtr<nsIReferrerInfo> referrerInfo =
 | |
|       ReferrerInfo::CreateForSVGResources(aFrame->GetContent()->OwnerDoc());
 | |
|   RefPtr<URLAndReferrerInfo> url =
 | |
|       new URLAndReferrerInfo(targetURI, referrerInfo);
 | |
| 
 | |
|   return static_cast<SVGMozElementObserver*>(
 | |
|              hashtable
 | |
|                  ->LookupOrInsertWith(
 | |
|                      url,
 | |
|                      [&] {
 | |
|                        return MakeRefPtr<SVGMozElementObserver>(
 | |
|                            url, aFrame,
 | |
|                            /* aWatchImage */ true);
 | |
|                      })
 | |
|                  .get())
 | |
|       ->GetAndObserveReferencedElement();
 | |
| }
 | |
| 
 | |
| Element* SVGObserverUtils::GetAndObserveBackgroundClip(nsIFrame* aFrame) {
 | |
|   bool found;
 | |
|   BackgroundClipRenderingObserver* obs =
 | |
|       aFrame->GetProperty(BackgroundClipObserverProperty(), &found);
 | |
|   if (!found) {
 | |
|     obs = new BackgroundClipRenderingObserver(aFrame);
 | |
|     NS_ADDREF(obs);
 | |
|     aFrame->AddProperty(BackgroundClipObserverProperty(), obs);
 | |
|   }
 | |
| 
 | |
|   return obs->GetAndObserveReferencedElement();
 | |
| }
 | |
| 
 | |
| SVGPaintServerFrame* SVGObserverUtils::GetAndObservePaintServer(
 | |
|     nsIFrame* aPaintedFrame, StyleSVGPaint nsStyleSVG::*aPaint) {
 | |
|   // If we're looking at a frame within SVG text, then we need to look up
 | |
|   // to find the right frame to get the painting property off.  We should at
 | |
|   // least look up past a text frame, and if the text frame's parent is the
 | |
|   // anonymous block frame, then we look up to its parent (the SVGTextFrame).
 | |
|   nsIFrame* paintedFrame = aPaintedFrame;
 | |
|   if (paintedFrame->GetContent()->IsText()) {
 | |
|     paintedFrame = paintedFrame->GetParent();
 | |
|     nsIFrame* grandparent = paintedFrame->GetParent();
 | |
|     if (grandparent && grandparent->IsSVGTextFrame()) {
 | |
|       paintedFrame = grandparent;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const nsStyleSVG* svgStyle = paintedFrame->StyleSVG();
 | |
|   if (!(svgStyle->*aPaint).kind.IsPaintServer()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<URLAndReferrerInfo> paintServerURL = ResolveURLUsingLocalRef(
 | |
|       paintedFrame, (svgStyle->*aPaint).kind.AsPaintServer());
 | |
| 
 | |
|   MOZ_ASSERT(aPaint == &nsStyleSVG::mFill || aPaint == &nsStyleSVG::mStroke);
 | |
|   PaintingPropertyDescriptor propDesc =
 | |
|       (aPaint == &nsStyleSVG::mFill) ? FillProperty() : StrokeProperty();
 | |
|   SVGPaintingProperty* property =
 | |
|       GetPaintingProperty(paintServerURL, paintedFrame, propDesc);
 | |
|   if (!property) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   nsIFrame* result = property->GetAndObserveReferencedFrame();
 | |
|   if (!result) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   LayoutFrameType type = result->Type();
 | |
|   if (type != LayoutFrameType::SVGLinearGradient &&
 | |
|       type != LayoutFrameType::SVGRadialGradient &&
 | |
|       type != LayoutFrameType::SVGPattern) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return static_cast<SVGPaintServerFrame*>(result);
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::UpdateEffects(nsIFrame* aFrame) {
 | |
|   NS_ASSERTION(aFrame->GetContent()->IsElement(),
 | |
|                "aFrame's content should be an element");
 | |
| 
 | |
|   aFrame->RemoveProperty(FilterProperty());
 | |
|   aFrame->RemoveProperty(MaskProperty());
 | |
|   aFrame->RemoveProperty(ClipPathProperty());
 | |
|   aFrame->RemoveProperty(MarkerStartProperty());
 | |
|   aFrame->RemoveProperty(MarkerMidProperty());
 | |
|   aFrame->RemoveProperty(MarkerEndProperty());
 | |
|   aFrame->RemoveProperty(FillProperty());
 | |
|   aFrame->RemoveProperty(StrokeProperty());
 | |
|   aFrame->RemoveProperty(BackgroundImageProperty());
 | |
| 
 | |
|   // Ensure that the filter is repainted correctly
 | |
|   // We can't do that in OnRenderingChange as the referenced frame may
 | |
|   // not be valid
 | |
|   GetOrCreateFilterObserverListForCSS(aFrame);
 | |
| 
 | |
|   if (aFrame->IsSVGGeometryFrame() &&
 | |
|       static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
 | |
|     // Set marker properties here to avoid reference loops
 | |
|     RefPtr<URLAndReferrerInfo> markerURL =
 | |
|         GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart);
 | |
|     GetEffectProperty(markerURL, aFrame, MarkerStartProperty());
 | |
|     markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
 | |
|     GetEffectProperty(markerURL, aFrame, MarkerMidProperty());
 | |
|     markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
 | |
|     GetEffectProperty(markerURL, aFrame, MarkerEndProperty());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::AddRenderingObserver(Element* aElement,
 | |
|                                             SVGRenderingObserver* aObserver) {
 | |
|   SVGRenderingObserverSet* observers = GetObserverSet(aElement);
 | |
|   if (!observers) {
 | |
|     observers = new SVGRenderingObserverSet();
 | |
|     aElement->SetProperty(nsGkAtoms::renderingobserverset, observers,
 | |
|                           nsINode::DeleteProperty<SVGRenderingObserverSet>);
 | |
|   }
 | |
|   aElement->SetHasRenderingObservers(true);
 | |
|   observers->Add(aObserver);
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::RemoveRenderingObserver(
 | |
|     Element* aElement, SVGRenderingObserver* aObserver) {
 | |
|   SVGRenderingObserverSet* observers = GetObserverSet(aElement);
 | |
|   if (observers) {
 | |
|     NS_ASSERTION(observers->Contains(aObserver),
 | |
|                  "removing observer from an element we're not observing?");
 | |
|     observers->Remove(aObserver);
 | |
|     if (observers->IsEmpty()) {
 | |
|       aElement->SetHasRenderingObservers(false);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::RemoveAllRenderingObservers(Element* aElement) {
 | |
|   SVGRenderingObserverSet* observers = GetObserverSet(aElement);
 | |
|   if (observers) {
 | |
|     observers->RemoveAll();
 | |
|     aElement->SetHasRenderingObservers(false);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::InvalidateRenderingObservers(nsIFrame* aFrame) {
 | |
|   NS_ASSERTION(!aFrame->GetPrevContinuation(),
 | |
|                "aFrame must be first continuation");
 | |
| 
 | |
|   nsIContent* content = aFrame->GetContent();
 | |
|   if (!content || !content->IsElement()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If the rendering has changed, the bounds may well have changed too:
 | |
|   aFrame->RemoveProperty(SVGUtils::ObjectBoundingBoxProperty());
 | |
| 
 | |
|   SVGRenderingObserverSet* observers = GetObserverSet(content->AsElement());
 | |
|   if (observers) {
 | |
|     observers->InvalidateAll();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Check ancestor SVG containers. The root frame cannot be of type
 | |
|   // eSVGContainer so we don't have to check f for null here.
 | |
|   for (nsIFrame* f = aFrame->GetParent();
 | |
|        f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) {
 | |
|     if (f->GetContent()->IsElement()) {
 | |
|       observers = GetObserverSet(f->GetContent()->AsElement());
 | |
|       if (observers) {
 | |
|         observers->InvalidateAll();
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::InvalidateDirectRenderingObservers(
 | |
|     Element* aElement, uint32_t aFlags /* = 0 */) {
 | |
|   if (nsIFrame* frame = aElement->GetPrimaryFrame()) {
 | |
|     // If the rendering has changed, the bounds may well have changed too:
 | |
|     frame->RemoveProperty(SVGUtils::ObjectBoundingBoxProperty());
 | |
|   }
 | |
| 
 | |
|   if (aElement->HasRenderingObservers()) {
 | |
|     SVGRenderingObserverSet* observers = GetObserverSet(aElement);
 | |
|     if (observers) {
 | |
|       if (aFlags & INVALIDATE_REFLOW) {
 | |
|         observers->InvalidateAllForReflow();
 | |
|       } else {
 | |
|         observers->InvalidateAll();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void SVGObserverUtils::InvalidateDirectRenderingObservers(
 | |
|     nsIFrame* aFrame, uint32_t aFlags /* = 0 */) {
 | |
|   nsIContent* content = aFrame->GetContent();
 | |
|   if (content && content->IsElement()) {
 | |
|     InvalidateDirectRenderingObservers(content->AsElement(), aFlags);
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsIURI> SVGObserverUtils::GetBaseURLForLocalRef(
 | |
|     nsIContent* content, nsIURI* aDocURI) {
 | |
|   MOZ_ASSERT(content);
 | |
| 
 | |
|   // Content is in a shadow tree.  If this URL was specified in the subtree
 | |
|   // referenced by the <use>, element, and that subtree came from a separate
 | |
|   // resource document, then we want the fragment-only URL to resolve to an
 | |
|   // element from the resource document.  Otherwise, the URL was specified
 | |
|   // somewhere in the document with the <use> element, and we want the
 | |
|   // fragment-only URL to resolve to an element in that document.
 | |
|   if (SVGUseElement* use = content->GetContainingSVGUseShadowHost()) {
 | |
|     if (nsIURI* originalURI = use->GetSourceDocURI()) {
 | |
|       bool isEqualsExceptRef = false;
 | |
|       aDocURI->EqualsExceptRef(originalURI, &isEqualsExceptRef);
 | |
|       if (isEqualsExceptRef) {
 | |
|         return do_AddRef(originalURI);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // For a local-reference URL, resolve that fragment against the current
 | |
|   // document that relative URLs are resolved against.
 | |
|   return do_AddRef(content->OwnerDoc()->GetDocumentURI());
 | |
| }
 | |
| 
 | |
| already_AddRefed<URLAndReferrerInfo> SVGObserverUtils::GetFilterURI(
 | |
|     nsIFrame* aFrame, const StyleFilter& aFilter) {
 | |
|   MOZ_ASSERT(!aFrame->StyleEffects()->mFilters.IsEmpty() ||
 | |
|              !aFrame->StyleEffects()->mBackdropFilters.IsEmpty());
 | |
|   MOZ_ASSERT(aFilter.IsUrl());
 | |
|   return ResolveURLUsingLocalRef(aFrame, aFilter.AsUrl());
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 |