SVGObserverUtils.h (16925B)
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/. */ 6 7 #ifndef LAYOUT_SVG_SVGOBSERVERUTILS_H_ 8 #define LAYOUT_SVG_SVGOBSERVERUTILS_H_ 9 10 #include "FrameProperties.h" 11 #include "mozilla/SVGIntegrationUtils.h" 12 #include "mozilla/dom/IDTracker.h" 13 #include "nsID.h" 14 #include "nsIFrame.h" // only for LayoutFrameType 15 #include "nsIMutationObserver.h" 16 #include "nsIReferrerInfo.h" 17 #include "nsISupports.h" 18 #include "nsISupportsImpl.h" 19 #include "nsStringFwd.h" 20 #include "nsStubMutationObserver.h" 21 #include "nsStyleStruct.h" 22 23 class nsAtom; 24 class nsCycleCollectionTraversalCallback; 25 class nsIFrame; 26 class nsIURI; 27 28 namespace mozilla { 29 class SVGClipPathFrame; 30 class SVGFilterFrame; 31 class SVGFilterObserver; 32 class SVGMarkerFrame; 33 class SVGMaskFrame; 34 class SVGPaintServerFrame; 35 36 namespace dom { 37 class CanvasRenderingContext2D; 38 class Element; 39 class SVGFEImageElement; 40 class SVGGeometryElement; 41 class SVGGraphicsElement; 42 class SVGMPathElement; 43 } // namespace dom 44 } // namespace mozilla 45 46 #define MOZILLA_ICANVASFILTEROBSERVER_IID \ 47 {0xd1c85f93, 0xd1ed, 0x4ea9, {0xa0, 0x39, 0x71, 0x62, 0xe4, 0x41, 0xf1, 0xa1}} 48 49 namespace mozilla { 50 51 class ISVGFilterObserverList : public nsISupports { 52 public: 53 NS_INLINE_DECL_STATIC_IID(MOZILLA_ICANVASFILTEROBSERVER_IID) 54 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 55 NS_DECL_CYCLE_COLLECTION_CLASS(ISVGFilterObserverList) 56 57 virtual const nsTArray<RefPtr<SVGFilterObserver>>& GetObservers() const = 0; 58 virtual void Detach() {} 59 60 protected: 61 virtual ~ISVGFilterObserverList() = default; 62 }; 63 64 /** 65 * This interface allows us to be notified when a piece of SVG content is 66 * re-rendered. 67 * 68 * Concrete implementations of this base class need to implement 69 * GetReferencedElementWithoutObserving to specify the SVG element that 70 * they'd like to monitor for rendering changes, and they need to implement 71 * OnRenderingChange to specify how we'll react when that content gets 72 * re-rendered. They also need to implement a constructor and destructor, 73 * which should call StartObserving and StopObserving, respectively. 74 * 75 * The referenced element is generally looked up and stored during 76 * construction. If the referenced element is in an extenal SVG resource 77 * document, the lookup code will initiate loading of the external resource and 78 * OnRenderingChange will be called once the element in the external resource 79 * is available. 80 * 81 * Although the referenced element may be found and stored during construction, 82 * observing for rendering changes does not start until requested. 83 */ 84 class SVGRenderingObserver : public nsStubMutationObserver { 85 protected: 86 virtual ~SVGRenderingObserver() = default; 87 88 public: 89 using Element = dom::Element; 90 91 SVGRenderingObserver(uint32_t aCallbacks = kAttributeChanged | 92 kContentAppended | 93 kContentInserted | 94 kContentWillBeRemoved) { 95 SetEnabledCallbacks(aCallbacks); 96 } 97 98 // nsIMutationObserver 99 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 100 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED 101 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED 102 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 103 104 /** 105 * Called when non-DOM-mutation changes to the observed element should likely 106 * cause the rendering of our observer to change. This includes changes to 107 * CSS computed values, but also changes to rendering observers that the 108 * observed element itself may have (for example, when we're being used to 109 * observe an SVG pattern, and an element in that pattern references and 110 * observes a gradient that has changed). 111 */ 112 void OnNonDOMMutationRenderingChange(); 113 114 // When a SVGRenderingObserver list gets forcibly cleared, it uses this 115 // callback to notify every observer that's cleared from it, so they can 116 // react. 117 void NotifyEvictedFromRenderingObserverSet(); 118 119 nsIFrame* GetAndObserveReferencedFrame(); 120 /** 121 * @param aOK this is only for the convenience of callers. We set *aOK to 122 * false if the frame is the wrong type 123 */ 124 nsIFrame* GetAndObserveReferencedFrame(mozilla::LayoutFrameType aFrameType, 125 bool* aOK); 126 127 Element* GetAndObserveReferencedElement(); 128 129 virtual bool ObservesReflow() { return false; } 130 131 protected: 132 void StartObserving(); 133 void StopObserving(); 134 135 /** 136 * Called whenever the rendering of the observed element may have changed. 137 * 138 * More specifically, this method is called whenever DOM mutation occurs in 139 * the observed element's subtree, or whenever 140 * SVGObserverUtils::InvalidateRenderingObservers or 141 * SVGObserverUtils::InvalidateDirectRenderingObservers is called for the 142 * observed element's frame. 143 * 144 * Subclasses should override this method to handle rendering changes 145 * appropriately. 146 */ 147 virtual void OnRenderingChange() = 0; 148 149 virtual Element* GetReferencedElementWithoutObserving() = 0; 150 151 #ifdef DEBUG 152 void DebugObserverSet(); 153 #endif 154 155 // Whether we're in our observed element's observer set at this time. 156 bool mInObserverSet = false; 157 }; 158 159 class SVGObserverUtils { 160 public: 161 using CanvasRenderingContext2D = dom::CanvasRenderingContext2D; 162 using Element = dom::Element; 163 using SVGGeometryElement = dom::SVGGeometryElement; 164 using SVGGraphicsElement = dom::SVGGraphicsElement; 165 using HrefToTemplateCallback = const std::function<void(nsAString&)>&; 166 167 /** 168 * Ensures that that if the given frame requires any resources that are in 169 * SVG resource documents that the loading of those documents is initiated. 170 * This does not make aFrame start to observe any elements that it 171 * references. 172 */ 173 static void InitiateResourceDocLoads(nsIFrame* aFrame); 174 175 /** 176 * Called when changes to an element (e.g. CSS property changes) cause its 177 * frame to start/stop referencing (or reference different) SVG resource 178 * elements. (_Not_ called for changes to referenced resource elements.) 179 * 180 * This function handles such changes by discarding _all_ the frame's SVG 181 * effects frame properties (causing those properties to stop watching their 182 * target element). It also synchronously (re)creates the filter and marker 183 * frame properties (XXX why not the other properties?), which makes it 184 * useful for initializing those properties during first reflow. 185 * 186 * XXX rename to something more meaningful like RefreshResourceReferences? 187 */ 188 static void UpdateEffects(nsIFrame* aFrame); 189 190 /* 191 * Returns true if the frame or any of its ancestors have rendering observers. 192 */ 193 static bool SelfOrAncestorHasRenderingObservers(const nsIFrame* aFrame); 194 195 /** 196 * @param aFrame must be a first-continuation. 197 */ 198 static void AddRenderingObserver(Element* aElement, 199 SVGRenderingObserver* aObserver); 200 /** 201 * @param aFrame must be a first-continuation. 202 */ 203 static void RemoveRenderingObserver(Element* aElement, 204 SVGRenderingObserver* aObserver); 205 206 /** 207 * Removes all rendering observers from aElement. 208 */ 209 static void RemoveAllRenderingObservers(Element* aElement); 210 211 /** 212 * This can be called on any frame. We invalidate the observers of aFrame's 213 * element, if any, or else walk up to the nearest observable SVG parent 214 * frame with observers and invalidate them instead. 215 * 216 * Note that this method is very different to e.g. 217 * MutationObservers::AttributeChanged which walks up the content node tree 218 * all the way to the root node (not stopping if it encounters a non-container 219 * SVG node) invalidating all mutation observers (not just 220 * SVGRenderingObservers) on all nodes along the way (not just the first 221 * node it finds with observers). In other words, by doing all the 222 * things in parentheses in the preceding sentence, this method uses 223 * knowledge about our implementation and what can be affected by SVG effects 224 * to make invalidation relatively lightweight when an SVG effect changes. 225 */ 226 static void InvalidateRenderingObservers(nsIFrame* aFrame); 227 228 enum { INVALIDATE_REFLOW = 0x1, INVALIDATE_DESTROY = 0x2 }; 229 230 enum ReferenceState { 231 /// Has no references to SVG filters (may still have CSS filter functions!) 232 eHasNoRefs, 233 eHasRefsAllValid, 234 eHasRefsSomeInvalid, 235 }; 236 237 /** 238 * This can be called on any element or frame. Only direct observers of this 239 * (frame's) element, if any, are invalidated. 240 */ 241 static void InvalidateDirectRenderingObservers(Element* aElement, 242 uint32_t aFlags = 0); 243 static void InvalidateDirectRenderingObservers(nsIFrame* aFrame, 244 uint32_t aFlags = 0); 245 246 /** 247 * Get the paint server for aPaintedFrame. 248 */ 249 static SVGPaintServerFrame* GetAndObservePaintServer( 250 nsIFrame* aPaintedFrame, mozilla::StyleSVGPaint nsStyleSVG::* aPaint); 251 252 /** 253 * Get the start/mid/end-markers for the given frame, and add the frame as 254 * an observer to those markers. Returns true if at least one marker type is 255 * found, false otherwise. 256 */ 257 static bool GetAndObserveMarkers(nsIFrame* aMarkedFrame, 258 SVGMarkerFrame* (*aFrames)[3]); 259 260 /** 261 * Get the frames of the SVG filters applied to the given frame, and add the 262 * frame as an observer to those filter frames. 263 * 264 * NOTE! A return value of eHasNoRefs does NOT mean that there are no filters 265 * to be applied, only that there are no references to SVG filter elements. 266 * 267 * @param aIsBackdrop whether we're observing a backdrop-filter or a filter. 268 * 269 * XXX Callers other than ComputePostEffectsInkOverflowRect and 270 * SVGUtils::GetPostFilterInkOverflowRect should not need to initiate 271 * observing. If we have a bug that causes invalidation (which would remove 272 * observers) between reflow and painting, then we don't really want to 273 * re-add abservers during painting. That has the potential to hide logic 274 * bugs, or cause later invalidation problems. However, let's not change 275 * that behavior just yet due to the regression potential. 276 */ 277 static ReferenceState GetAndObserveFilters( 278 nsIFrame* aFilteredFrame, nsTArray<SVGFilterFrame*>* aFilterFrames, 279 StyleFilterType aStyleFilterType = StyleFilterType::Filter); 280 281 /* 282 * NOTE! canvas doesn't have backdrop-filters so there's no StyleFilterType 283 * parameter. 284 */ 285 static ReferenceState GetAndObserveFilters( 286 ISVGFilterObserverList* aObserverList, 287 nsTArray<SVGFilterFrame*>* aFilterFrames); 288 289 /** 290 * If the given frame is already observing SVG filters, this function gets 291 * those filters. If the frame is not already observing filters this 292 * function assumes that it doesn't have anything to observe. 293 */ 294 static ReferenceState GetFiltersIfObserving( 295 nsIFrame* aFilteredFrame, nsTArray<SVGFilterFrame*>* aFilterFrames); 296 297 /** 298 * Starts observing filters for a <canvas> element's CanvasRenderingContext2D. 299 * 300 * Returns a RAII object that the caller should make sure is released once 301 * the CanvasRenderingContext2D is no longer using them (that is, when the 302 * CanvasRenderingContext2D "drawing style state" on which the filters were 303 * set is destroyed or has its filter style reset). 304 * 305 * XXXjwatt: It's a bit unfortunate that both we and 306 * CanvasRenderingContext2D::UpdateFilter process the list of StyleFilter 307 * objects separately. It would be better to refactor things so that we only 308 * do that work once. 309 */ 310 static already_AddRefed<ISVGFilterObserverList> 311 ObserveFiltersForCanvasContext(CanvasRenderingContext2D* aContext, 312 Element* aCanvasElement, 313 Span<const StyleFilter> aFilters); 314 315 /** 316 * Get the frame of the SVG clipPath applied to aClippedFrame, if any, and 317 * set up aClippedFrame as a rendering observer of the clipPath's frame, to 318 * be invalidated if it changes. 319 * 320 * Currently we only have support for 'clip-path' with a single item, but the 321 * spec. now says 'clip-path' can be set to an arbitrary number of items. 322 * Once we support that, aClipPathFrame will need to be an nsTArray as it 323 * is for 'filter' and 'mask'. Currently a return value of eHasNoRefs means 324 * that there is no clipping at all, but once we support more than one item 325 * then - as for filter and mask - we could still have basic shape clipping 326 * to apply even if there are no references to SVG clipPath elements. 327 * 328 * Note that, unlike for filters, a reference to an ID that doesn't exist 329 * is not invalid for clip-path or mask. We will return eHasNoRefs in that 330 * case. 331 */ 332 static ReferenceState GetAndObserveClipPath( 333 nsIFrame* aClippedFrame, SVGClipPathFrame** aClipPathFrame); 334 335 /** 336 * Get the element of the SVG Shape element, if any, and set up |aFrame| as a 337 * rendering observer of the geometry frame, to post a restyle if it changes. 338 * 339 * We use this function to resolve offset-path:url() and build the equivalent 340 * path from this shape element, and generate the transformation from for CSS 341 * Motion. 342 */ 343 static SVGGeometryElement* GetAndObserveGeometry(nsIFrame* aFrame); 344 345 /** 346 * If masking is applied to aMaskedFrame, gets an array of any SVG masks 347 * that are referenced, setting up aMaskFrames as a rendering observer of 348 * those masks (if any). 349 * 350 * NOTE! A return value of eHasNoRefs does NOT mean that there are no masks 351 * to be applied, only that there are no references to SVG mask elements. 352 * 353 * Note that, unlike for filters, a reference to an ID that doesn't exist 354 * is not invalid for clip-path or mask. We will return eHasNoRefs in that 355 * case. 356 */ 357 static ReferenceState GetAndObserveMasks( 358 nsIFrame* aMaskedFrame, nsTArray<SVGMaskFrame*>* aMaskFrames); 359 360 /** 361 * Get the SVGGeometryElement that is referenced by aTextPathFrame, and make 362 * aTextPathFrame start observing rendering changes to that element. 363 */ 364 static SVGGeometryElement* GetAndObserveTextPathsPath( 365 nsIFrame* aTextPathFrame); 366 367 /** 368 * Make aTextPathFrame stop observing rendering changes to the 369 * SVGGeometryElement that it references, if any. 370 */ 371 static void RemoveTextPathObserver(nsIFrame* aTextPathFrame); 372 373 /** 374 * Get the SVGGraphicsElement that is referenced by aSVGFEImageElement, and 375 * make aSVGFEImageElement start observing rendering changes to that element. 376 */ 377 static SVGGraphicsElement* GetAndObserveFEImageContent( 378 dom::SVGFEImageElement* aSVGFEImagrElement); 379 380 static void TraverseFEImageObserver( 381 dom::SVGFEImageElement* aSVGFEImageElement, 382 nsCycleCollectionTraversalCallback* aCB); 383 384 /** 385 * Get the SVGGeometryElement that is referenced by aSVGMPathElement, and 386 * make aSVGMPathElement start observing rendering changes to that element. 387 */ 388 static SVGGeometryElement* GetAndObserveMPathsPath( 389 dom::SVGMPathElement* aSVGMPathElement); 390 391 static void TraverseMPathObserver(dom::SVGMPathElement* aSVGMPathElement, 392 nsCycleCollectionTraversalCallback* aCB); 393 394 /** 395 * Gets the nsIFrame of a referenced SVG "template" element, if any, and 396 * makes aFrame start observing rendering changes to the template element. 397 * 398 * Template elements: some elements like gradients, pattern or filter can 399 * reference another element of the same type using their 'href' attribute, 400 * and use that element as a template that provides attributes or content 401 * that is missing from the referring element. 402 * 403 * The frames that this function is called for do not have a common base 404 * class, which is why it is necessary to pass in a function that can be 405 * used as a callback to lazily get the href value, if necessary. 406 */ 407 static nsIFrame* GetAndObserveTemplate(nsIFrame* aFrame, 408 HrefToTemplateCallback aGetHref); 409 410 static void RemoveTemplateObserver(nsIFrame* aFrame); 411 412 /** 413 * Gets an arbitrary element and starts observing it. Used to implement 414 * '-moz-element'. 415 * 416 * Note that bug 1496065 has been filed to remove support for referencing 417 * arbitrary elements using '-moz-element'. 418 */ 419 static Element* GetAndObserveBackgroundImage(nsIFrame* aFrame, 420 const nsAtom* aHref); 421 422 /** 423 * Gets an arbitrary element and starts observing it. Used to detect 424 * invalidation changes for background-clip:text. 425 */ 426 static Element* GetAndObserveBackgroundClip(nsIFrame* aFrame); 427 }; 428 429 } // namespace mozilla 430 431 #endif // LAYOUT_SVG_SVGOBSERVERUTILS_H_