tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_