tor-browser

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

SVGLengthList.h (12297B)


      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 DOM_SVG_SVGLENGTHLIST_H_
      8 #define DOM_SVG_SVGLENGTHLIST_H_
      9 
     10 #include "SVGElement.h"
     11 #include "SVGLength.h"
     12 #include "nsCOMPtr.h"
     13 #include "nsDebug.h"
     14 #include "nsIContent.h"
     15 #include "nsINode.h"
     16 #include "nsIWeakReferenceUtils.h"
     17 #include "nsTArray.h"
     18 
     19 namespace mozilla {
     20 
     21 namespace dom {
     22 class DOMSVGLength;
     23 class DOMSVGLengthList;
     24 }  // namespace dom
     25 
     26 /**
     27 * ATTENTION! WARNING! WATCH OUT!!
     28 *
     29 * Consumers that modify objects of this type absolutely MUST keep the DOM
     30 * wrappers for those lists (if any) in sync!! That's why this class is so
     31 * locked down.
     32 *
     33 * The DOM wrapper class for this class is DOMSVGLengthList.
     34 */
     35 class SVGLengthList {
     36  friend class dom::DOMSVGLength;
     37  friend class dom::DOMSVGLengthList;
     38  friend class SVGAnimatedLengthList;
     39 
     40 public:
     41  SVGLengthList() = default;
     42  ~SVGLengthList() = default;
     43 
     44  SVGLengthList& operator=(const SVGLengthList& aOther) {
     45    mLengths.ClearAndRetainStorage();
     46    // Best-effort, really.
     47    (void)mLengths.AppendElements(aOther.mLengths, fallible);
     48    return *this;
     49  }
     50 
     51  SVGLengthList(const SVGLengthList& aOther) { *this = aOther; }
     52 
     53  // Only methods that don't make/permit modification to this list are public.
     54  // Only our friend classes can access methods that may change us.
     55 
     56  /// This may return an incomplete string on OOM, but that's acceptable.
     57  void GetValueAsString(nsAString& aValue) const;
     58 
     59  bool IsEmpty() const { return mLengths.IsEmpty(); }
     60 
     61  uint32_t Length() const { return mLengths.Length(); }
     62 
     63  const SVGLength& operator[](uint32_t aIndex) const {
     64    return mLengths[aIndex];
     65  }
     66 
     67  bool operator==(const SVGLengthList& rhs) const;
     68 
     69  bool SetCapacity(uint32_t size) {
     70    return mLengths.SetCapacity(size, fallible);
     71  }
     72 
     73  void Compact() { mLengths.Compact(); }
     74 
     75  // Access to methods that can modify objects of this type is deliberately
     76  // limited. This is to reduce the chances of someone modifying objects of
     77  // this type without taking the necessary steps to keep DOM wrappers in sync.
     78  // If you need wider access to these methods, consider adding a method to
     79  // SVGAnimatedLengthList and having that class act as an intermediary so it
     80  // can take care of keeping DOM wrappers in sync.
     81 
     82 protected:
     83  /**
     84   * This may fail on OOM if the internal capacity needs to be increased, in
     85   * which case the list will be left unmodified.
     86   */
     87  nsresult CopyFrom(const SVGLengthList&);
     88  void SwapWith(SVGLengthList& aOther) {
     89    mLengths.SwapElements(aOther.mLengths);
     90  }
     91 
     92  SVGLength& operator[](uint32_t aIndex) { return mLengths[aIndex]; }
     93 
     94  /**
     95   * This may fail (return false) on OOM if the internal capacity is being
     96   * increased, in which case the list will be left unmodified.
     97   */
     98  bool SetLength(uint32_t aNumberOfItems) {
     99    return mLengths.SetLength(aNumberOfItems, fallible);
    100  }
    101 
    102 private:
    103  // Marking the following private only serves to show which methods are only
    104  // used by our friend classes (as opposed to our subclasses) - it doesn't
    105  // really provide additional safety.
    106 
    107  nsresult SetValueFromString(const nsAString& aValue);
    108 
    109  void Clear() { mLengths.Clear(); }
    110 
    111  bool InsertItem(uint32_t aIndex, const SVGLength& aLength) {
    112    if (aIndex >= mLengths.Length()) aIndex = mLengths.Length();
    113    return !!mLengths.InsertElementAt(aIndex, aLength, fallible);
    114  }
    115 
    116  void ReplaceItem(uint32_t aIndex, const SVGLength& aLength) {
    117    MOZ_ASSERT(aIndex < mLengths.Length(),
    118               "DOM wrapper caller should have raised INDEX_SIZE_ERR");
    119    mLengths[aIndex] = aLength;
    120  }
    121 
    122  void RemoveItem(uint32_t aIndex) {
    123    MOZ_ASSERT(aIndex < mLengths.Length(),
    124               "DOM wrapper caller should have raised INDEX_SIZE_ERR");
    125    mLengths.RemoveElementAt(aIndex);
    126  }
    127 
    128  bool AppendItem(SVGLength aLength) {
    129    return !!mLengths.AppendElement(aLength, fallible);
    130  }
    131 
    132 protected:
    133  /* Rationale for using nsTArray<SVGLength> and not nsTArray<SVGLength, 1>:
    134   *
    135   * It might seem like we should use AutoTArray<SVGLength, 1> instead of
    136   * nsTArray<SVGLength>. That would preallocate space for one SVGLength and
    137   * avoid an extra memory allocation call in the common case of the 'x'
    138   * and 'y' attributes each containing a single length (and the 'dx' and 'dy'
    139   * attributes being empty). However, consider this:
    140   *
    141   * An empty nsTArray uses sizeof(Header*). An AutoTArray<class E,
    142   * uint32_t N> on the other hand uses sizeof(Header*) +
    143   * (2 * sizeof(uint32_t)) + (N * sizeof(E)), which for one SVGLength is
    144   * sizeof(Header*) + 16 bytes.
    145   *
    146   * Now consider that for text elements we have four length list attributes
    147   * (x, y, dx, dy), each of which can have a baseVal and an animVal list. If
    148   * we were to go the AutoTArray<SVGLength, 1> route for each of these, we'd
    149   * end up using at least 160 bytes for these four attributes alone, even
    150   * though we only need storage for two SVGLengths (16 bytes) in the common
    151   * case!!
    152   *
    153   * A compromise might be to template SVGLengthList to allow
    154   * SVGAnimatedLengthList to preallocate space for an SVGLength for the
    155   * baseVal lists only, and that would cut the space used by the four
    156   * attributes to 96 bytes. Taking that even further and templating
    157   * SVGAnimatedLengthList too in order to only use nsTArray for 'dx' and 'dy'
    158   * would reduce the storage further to 64 bytes. Having different types makes
    159   * things more complicated for code that needs to look at the lists though.
    160   * In fact it also makes things more complicated when it comes to storing the
    161   * lists.
    162   *
    163   * It may be worth considering using nsAttrValue for length lists instead of
    164   * storing them directly on the element.
    165   */
    166  FallibleTArray<SVGLength> mLengths;
    167 };
    168 
    169 /**
    170 * This SVGLengthList subclass is for SVGLengthListSMILType which needs to know
    171 * which element and attribute a length list belongs to so that it can convert
    172 * between unit types if necessary.
    173 */
    174 class SVGLengthListAndInfo : public SVGLengthList {
    175 public:
    176  SVGLengthListAndInfo() : mElement(nullptr), mAxis(0), mCanZeroPadList(true) {}
    177 
    178  SVGLengthListAndInfo(dom::SVGElement* aElement, uint8_t aAxis,
    179                       bool aCanZeroPadList)
    180      : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))),
    181        mAxis(aAxis),
    182        mCanZeroPadList(aCanZeroPadList) {}
    183 
    184  void SetInfo(dom::SVGElement* aElement, uint8_t aAxis, bool aCanZeroPadList) {
    185    mElement = do_GetWeakReference(static_cast<nsINode*>(aElement));
    186    mAxis = aAxis;
    187    mCanZeroPadList = aCanZeroPadList;
    188  }
    189 
    190  dom::SVGElement* Element() const {
    191    nsCOMPtr<nsIContent> e = do_QueryReferent(mElement);
    192    return static_cast<dom::SVGElement*>(e.get());
    193  }
    194 
    195  /**
    196   * Returns true if this object is an "identity" value, from the perspective
    197   * of SMIL. In other words, returns true until the initial value set up in
    198   * SVGLengthListSMILType::InitValue() has been changed with a SetInfo() call.
    199   */
    200  bool IsIdentity() const {
    201    if (!mElement) {
    202      MOZ_ASSERT(IsEmpty(), "target element propagation failure");
    203      return true;
    204    }
    205    return false;
    206  }
    207 
    208  uint8_t Axis() const {
    209    MOZ_ASSERT(mElement, "Axis() isn't valid");
    210    return mAxis;
    211  }
    212 
    213  /**
    214   * The value returned by this function depends on which attribute this object
    215   * is for. If appending a list of zeros to the attribute's list would have no
    216   * effect on rendering (e.g. the attributes 'dx' and 'dy' on <text>), then
    217   * this method will return true. If appending a list of zeros to the
    218   * attribute's list could *change* rendering (e.g. the attributes 'x' and 'y'
    219   * on <text>), then this method will return false.
    220   *
    221   * The reason that this method exists is because the SMIL code needs to know
    222   * what to do when it's asked to animate between lists of different length.
    223   * If this method returns true, then it can zero pad the short list before
    224   * carrying out its operations. However, in the case of the 'x' and 'y'
    225   * attributes on <text>, zero would mean "zero in the current coordinate
    226   * system", whereas we would want to pad shorter lists with the coordinates
    227   * at which glyphs would otherwise lie, which is almost certainly not zero!
    228   * Animating from/to zeros in this case would produce terrible results.
    229   *
    230   * Currently SVGLengthListSMILType simply disallows (drops) animation between
    231   * lists of different length if it can't zero pad a list. This is to avoid
    232   * having some authors create content that depends on undesirable behaviour
    233   * (which would make it difficult for us to fix the behavior in future). At
    234   * some point it would be nice to implement a callback to allow this code to
    235   * determine padding values for lists that can't be zero padded. See
    236   * https://bugzilla.mozilla.org/show_bug.cgi?id=573431
    237   */
    238  bool CanZeroPadList() const {
    239    // NS_ASSERTION(mElement, "CanZeroPadList() isn't valid");
    240    return mCanZeroPadList;
    241  }
    242 
    243  // For the SMIL code. See comment in SVGLengthListSMILType::Add().
    244  void SetCanZeroPadList(bool aCanZeroPadList) {
    245    mCanZeroPadList = aCanZeroPadList;
    246  }
    247 
    248  nsresult CopyFrom(const SVGLengthListAndInfo& rhs) {
    249    mElement = rhs.mElement;
    250    mAxis = rhs.mAxis;
    251    mCanZeroPadList = rhs.mCanZeroPadList;
    252    return SVGLengthList::CopyFrom(rhs);
    253  }
    254 
    255  // Instances of this special subclass do not have DOM wrappers that we need
    256  // to worry about keeping in sync, so it's safe to expose any hidden base
    257  // class methods required by the SMIL code, as we do below.
    258 
    259  /**
    260   * Exposed so that SVGLengthList baseVals can be copied to
    261   * SVGLengthListAndInfo objects. Note that callers should also call
    262   * SetInfo() when using this method!
    263   */
    264  nsresult CopyFrom(const SVGLengthList& rhs) {
    265    return SVGLengthList::CopyFrom(rhs);
    266  }
    267  const SVGLength& operator[](uint32_t aIndex) const {
    268    return SVGLengthList::operator[](aIndex);
    269  }
    270  SVGLength& operator[](uint32_t aIndex) {
    271    return SVGLengthList::operator[](aIndex);
    272  }
    273  bool SetLength(uint32_t aNumberOfItems) {
    274    return SVGLengthList::SetLength(aNumberOfItems);
    275  }
    276 
    277 private:
    278  // We must keep a weak reference to our element because we may belong to a
    279  // cached baseVal SMILValue. See the comments starting at:
    280  // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
    281  // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497
    282  nsWeakPtr mElement;
    283  uint8_t mAxis;
    284  bool mCanZeroPadList;
    285 };
    286 
    287 /**
    288 * This class wraps SVGLengthList objects to allow frame consumers to process
    289 * SVGLengthList objects as if they were simply a list of float values in user
    290 * units. When consumers request the value at a given index, this class
    291 * dynamically converts the corresponding SVGLength from its actual unit and
    292 * returns its value in user units.
    293 *
    294 * Consumers should check that the user unit values returned are finite. Even
    295 * if the consumer can guarantee the list's element has a valid viewport
    296 * ancestor to resolve percentage units against, and a valid presContext and
    297 * ComputedStyle to resolve absolute and em/ex units against, unit conversions
    298 * could still overflow. In that case the value returned will be
    299 * numeric_limits<float>::quiet_NaN().
    300 */
    301 class MOZ_STACK_CLASS SVGUserUnitList {
    302 public:
    303  SVGUserUnitList() : mList(nullptr), mElement(nullptr), mAxis(0) {}
    304 
    305  void Init(const SVGLengthList* aList, const dom::SVGElement* aElement,
    306            uint8_t aAxis) {
    307    mList = aList;
    308    mElement = aElement;
    309    mAxis = aAxis;
    310  }
    311 
    312  bool IsEmpty() const { return mList->IsEmpty(); }
    313 
    314  uint32_t Length() const { return mList->Length(); }
    315 
    316  /// This may return a non-finite value
    317  float operator[](uint32_t aIndex) const {
    318    return (*mList)[aIndex].GetValueInPixelsWithZoom(mElement, mAxis);
    319  }
    320 
    321  bool HasPercentageValueAt(uint32_t aIndex) const {
    322    return (*mList)[aIndex].IsPercentage();
    323  }
    324 
    325 private:
    326  const SVGLengthList* mList;
    327  const dom::SVGElement* mElement;
    328  uint8_t mAxis;
    329 };
    330 
    331 }  // namespace mozilla
    332 
    333 #endif  // DOM_SVG_SVGLENGTHLIST_H_