DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
/* -*- 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/. */

#ifndef MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
#define MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__

#include "nsCycleCollectionParticipant.h"
#include "SVGElement.h"
#include "mozilla/Attributes.h"
#include "mozilla/RefPtr.h"

namespace mozilla {

class SVGAnimatedLengthList;
class SVGLengthList;

namespace dom {

class DOMSVGLengthList;

/**
 * Class DOMSVGAnimatedLengthList
 *
 * This class is used to create the DOM tearoff objects that wrap internal
 * SVGAnimatedLengthList objects. We have this internal-DOM split because DOM
 * classes are relatively heavy-weight objects with non-optimal interfaces for
 * internal code, and they're relatively infrequently used. Having separate
 * internal and DOM classes does add complexity - especially for lists where
 * the internal list and DOM lists (and their items) need to be kept in sync -
 * but it keeps the internal classes light and fast, and in 99% of cases
 * they're all that's used. DOM wrappers are only instantiated when script
 * demands it.
 *
 * Ownership model:
 *
 * The diagram below shows the ownership model between the various DOM objects
 * in the tree of DOM objects that correspond to an SVG length list attribute.
 * The angled brackets ">" and "<" denote a reference from one object to
 * another, where the "!" character denotes a strong reference, and the "~"
 * character denotes a weak reference.
 *
 *      .----<!----.                .----<!----.        .----<!----.
 *      |          |                |          |        |          |
 *   element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
 *
 * Rationale:
 *
 * The following three paragraphs explain the main three requirements that must
 * be met by any design. These are followed by an explanation of the rationale
 * behind our particular design.
 *
 * 1: DOMSVGAnimatedLengthList, DOMSVGLengthLists and DOMSVGLength get to their
 * internal counterparts via their element, and they use their element to send
 * out appropriate notifications when they change. Because of this, having
 * their element disappear out from under them would be very bad. To keep their
 * element alive at least as long as themselves, each of these classes must
 * contain a _strong_ reference (directly or indirectly) to their element.
 *
 * 2: Another central requirement of any design is the SVG specification's
 * requirement that script must always be given the exact same objects each
 * time it accesses a given object in a DOM object tree corresponding to an SVG
 * length list attribute. In practice "always" actually means "whenever script
 * has kept a references to a DOM object it previously accessed", since a
 * script will only be able to detect any difference in object identity if it
 * has a previous reference to compare against.
 *
 * 3: The wiggle room in the "same object" requirement leads us to a third
 * (self imposed) requirement: if script no longer has a reference to a given
 * DOM object from an object tree corresponding to an SVG length list
 * attribute, and if that object doesn't currently have any descendants, then
 * that object should be released to free up memory.
 *
 * To help in understanding our current design, consider this BROKEN design:
 *
 *      .-------------------------------<!-------------------------.
 *      |--------------------<!----------------.                   |
 *      |----<!----.                           |                   |
 *      |          |                           |                   |
 *   element ~> DOMSVGAnimatedLengthList !> DOMSVGLengthList !> DOMSVGLength
 *
 * Having all the objects keep a reference directly to their element like this
 * would reduce the number of dereferences that they need to make to get their
 * internal counterpart. However, this design does not meet the "same object"
 * requirement of the SVG specification. If script keeps a reference to a
 * DOMSVGLength or DOMSVGLengthList object, but not to that object's
 * DOMSVGAnimatedLengthList, then the DOMSVGAnimatedLengthList may be garbage
 * collected. We'd then have no way to return the same DOMSVGLength /
 * DOMSVGLengthList object that the script has a reference to if the script
 * went looking for it via the DOMSVGAnimatedLengthList property on the
 * element - we'd end up creating a fresh DOMSVGAnimatedLengthList, with no
 * knowlegde of the existing DOMSVGLengthList or DOMSVGLength object.
 *
 * The way we solve this problem is by making sure that parent objects cannot
 * die until all their children are dead by having child objects hold a strong
 * reference to their parent object. Note that this design means that the child
 * objects hold a strong reference to their element too, albeit indirectly via
 * the strong reference to their parent object:
 *
 *      .----<!----.                .----<!----.        .----<!----.
 *      |          |                |          |        |          |
 *   element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
 *
 * One drawback of this design is that objects must look up their parent
 * chain to find their element, but that overhead is relatively small.
 */
class DOMSVGAnimatedLengthList final : public nsWrapperCache {
  friend class DOMSVGLengthList;

 public:
  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DOMSVGAnimatedLengthList)
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DOMSVGAnimatedLengthList)

  /**
   * Factory method to create and return a DOMSVGAnimatedLengthList wrapper
   * for a given internal SVGAnimatedLengthList object. The factory takes care
   * of caching the object that it returns so that the same object can be
   * returned for the given SVGAnimatedLengthList each time it is requested.
   * The cached object is only removed from the cache when it is destroyed due
   * to there being no more references to it or to any of its descendant
   * objects. If that happens, any subsequent call requesting the DOM wrapper
   * for the SVGAnimatedLengthList will naturally result in a new
   * DOMSVGAnimatedLengthList being returned.
   */
  static already_AddRefed<DOMSVGAnimatedLengthList> GetDOMWrapper(
      SVGAnimatedLengthList* aList, dom::SVGElement* aElement,
      uint8_t aAttrEnum, uint8_t aAxis);

  /**
   * This method returns the DOMSVGAnimatedLengthList wrapper for an internal
   * SVGAnimatedLengthList object if it currently has a wrapper. If it does
   * not, then nullptr is returned.
   */
  static DOMSVGAnimatedLengthList* GetDOMWrapperIfExists(
      SVGAnimatedLengthList* aList);

  /**
   * Called by internal code to notify us when we need to sync the length of
   * our baseVal DOM list with its internal list. This is called just prior to
   * the length of the internal baseVal list being changed so that any DOM list
   * items that need to be removed from the DOM list can first get their values
   * from their internal counterpart.
   *
   * The only time this method could fail is on OOM when trying to increase the
   * length of the DOM list. If that happens then this method simply clears the
   * list and returns. Callers just proceed as normal, and we simply accept
   * that the DOM list will be empty (until successfully set to a new value).
   */
  void InternalBaseValListWillChangeTo(const SVGLengthList& aNewValue);
  void InternalAnimValListWillChangeTo(const SVGLengthList& aNewValue);

  /**
   * Returns true if our attribute is animating (in which case our animVal is
   * not simply a mirror of our baseVal).
   */
  bool IsAnimating() const;

  // WebIDL
  dom::SVGElement* GetParentObject() const { return mElement; }
  virtual JSObject* WrapObject(JSContext* aCx,
                               JS::Handle<JSObject*> aGivenProto) override;
  // These aren't weak refs because mBaseVal and mAnimVal are weak
  already_AddRefed<DOMSVGLengthList> BaseVal();
  already_AddRefed<DOMSVGLengthList> AnimVal();

 private:
  /**
   * Only our static GetDOMWrapper() factory method may create objects of our
   * type.
   */
  DOMSVGAnimatedLengthList(dom::SVGElement* aElement, uint8_t aAttrEnum,
                           uint8_t aAxis)
      : mBaseVal(nullptr),
        mAnimVal(nullptr),
        mElement(aElement),
        mAttrEnum(aAttrEnum),
        mAxis(aAxis) {}

  ~DOMSVGAnimatedLengthList();

  /// Get a reference to this DOM wrapper object's internal counterpart.
  SVGAnimatedLengthList& InternalAList();
  const SVGAnimatedLengthList& InternalAList() const;

  // Weak refs to our DOMSVGLengthList baseVal/animVal objects. These objects
  // are friends and take care of clearing these pointers when they die, making
  // these true weak references.
  DOMSVGLengthList* mBaseVal;
  DOMSVGLengthList* mAnimVal;

  // Strong ref to our element to keep it alive. We hold this not only for
  // ourself, but also for our base/animVal and all of their items.
  RefPtr<dom::SVGElement> mElement;

  uint8_t mAttrEnum;
  uint8_t mAxis;
};

}  // namespace dom
}  // namespace mozilla

#endif  // MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__