tor-browser

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

AttrArray.h (13057B)


      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 /*
      8 * Storage of the attributes of a DOM node.
      9 */
     10 
     11 #ifndef AttrArray_h___
     12 #define AttrArray_h___
     13 
     14 #include "mozilla/BindgenUniquePtr.h"
     15 #include "mozilla/MemoryReporting.h"
     16 #include "mozilla/Span.h"
     17 #include "mozilla/dom/BorrowedAttrInfo.h"
     18 #include "nsAttrName.h"
     19 #include "nsAttrValue.h"
     20 #include "nsCaseTreatment.h"
     21 #include "nscore.h"
     22 
     23 namespace mozilla {
     24 class AttributeStyles;
     25 struct StyleLockedDeclarationBlock;
     26 
     27 namespace dom {
     28 class Element;
     29 class ElementInternals;
     30 }  // namespace dom
     31 }  // namespace mozilla
     32 
     33 class AttrArray {
     34  using BorrowedAttrInfo = mozilla::dom::BorrowedAttrInfo;
     35 
     36  // Declare as friend to grant access to call SetAndSwapAttr.
     37  friend class mozilla::dom::Element;
     38  friend class mozilla::dom::ElementInternals;
     39 
     40 public:
     41  AttrArray() {
     42    // Initialize bloom filter.
     43    SetTaggedBloom(0x1ULL);
     44  }
     45  ~AttrArray() {
     46    // If mImpl contains a tagged bloom filter (bit 0 = 1), we must clear it
     47    // before the BindgenUniquePtr destructor tries to free it as a pointer.
     48    if (HasTaggedBloom()) {
     49      mImpl.release();
     50    }
     51  }
     52 
     53  bool HasAttrs() const { return !!AttrCount(); }
     54 
     55  uint32_t AttrCount() const { return HasImpl() ? GetImpl()->mAttrCount : 0; }
     56 
     57  const nsAttrValue* GetAttr(const nsAtom* aLocalName) const;
     58 
     59  const nsAttrValue* GetAttr(const nsAtom* aLocalName,
     60                             int32_t aNamespaceID) const;
     61  // As above but using a string attr name and always using
     62  // kNameSpaceID_None.  This is always case-sensitive.
     63  const nsAttrValue* GetAttr(const nsAString& aName) const;
     64  // Get an nsAttrValue by qualified name.  Can optionally do
     65  // ASCII-case-insensitive name matching.
     66  const nsAttrValue* GetAttr(const nsAString& aName,
     67                             nsCaseTreatment aCaseSensitive) const;
     68  const nsAttrValue* AttrAt(uint32_t aPos) const;
     69 
     70  // This stores the argument and clears the pending mapped attribute evaluation
     71  // bit, so after calling this IsPendingMappedAttributeEvaluation() is
     72  // guaranteed to return false.
     73  void SetMappedDeclarationBlock(
     74      already_AddRefed<mozilla::StyleLockedDeclarationBlock>);
     75 
     76  bool IsPendingMappedAttributeEvaluation() const {
     77    return HasImpl() && GetImpl()->mMappedAttributeBits & 1;
     78  }
     79 
     80  mozilla::StyleLockedDeclarationBlock* GetMappedDeclarationBlock() const {
     81    return HasImpl() ? GetImpl()->GetMappedDeclarationBlock() : nullptr;
     82  }
     83 
     84  // Remove the attr at position aPos.  The value of the attr is placed in
     85  // aValue; any value that was already in aValue is destroyed.
     86  nsresult RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
     87 
     88  // Returns attribute name at given position, *not* out-of-bounds safe
     89  const nsAttrName* AttrNameAt(uint32_t aPos) const;
     90 
     91  // Returns the attribute info at a given position, *not* out-of-bounds safe
     92  BorrowedAttrInfo AttrInfoAt(uint32_t aPos) const;
     93 
     94  // If aPos is in bounds, set aResult to the attribute at the given position
     95  // without AddRefing it and return true. Otherwise, return false.
     96  [[nodiscard]] bool GetSafeAttrNameAt(uint32_t aPos,
     97                                       const nsAttrName** aResult) const;
     98 
     99  // If aPos is in bounds, return the attribute at the given position.
    100  // Otherwise, crash.
    101  const nsAttrName* GetSafeAttrNameAt(uint32_t aPos) const;
    102 
    103  const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
    104  int32_t IndexOfAttr(const nsAtom* aLocalName) const;
    105  int32_t IndexOfAttr(const nsAtom* aLocalName, int32_t aNamespaceID) const;
    106 
    107  void Compact();
    108 
    109  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
    110 
    111  // Mark the element as pending mapped attribute evaluation. This should be
    112  // called when a mapped attribute is changed (regardless of connectedness).
    113  bool MarkAsPendingPresAttributeEvaluation() {
    114    // It'd be great to be able to assert that HasImpl() is true or we're the
    115    // <body> or <svg> elements.
    116    if (MOZ_UNLIKELY(!HasImpl()) && !GrowBy(1)) {
    117      return false;
    118    }
    119    InfallibleMarkAsPendingPresAttributeEvaluation();
    120    return true;
    121  }
    122 
    123  // See above.
    124  void InfallibleMarkAsPendingPresAttributeEvaluation() {
    125    MOZ_ASSERT(HasImpl());
    126    GetImpl()->mMappedAttributeBits |= 1;
    127  }
    128 
    129  // Clear the servo declaration block on the mapped attributes, if any
    130  // Will assert off main thread
    131  void ClearMappedServoStyle();
    132 
    133  // Increases capacity (if necessary) to have enough space to accomodate the
    134  // unmapped attributes of |aOther|.
    135  nsresult EnsureCapacityToClone(const AttrArray& aOther);
    136 
    137  enum AttrValuesState { ATTR_MISSING = -1, ATTR_VALUE_NO_MATCH = -2 };
    138  using AttrValuesArray = nsStaticAtom* const;
    139  int32_t FindAttrValueIn(int32_t aNameSpaceID, const nsAtom* aName,
    140                          AttrValuesArray* aValues,
    141                          nsCaseTreatment aCaseSensitive) const;
    142 
    143  inline bool GetAttr(int32_t aNameSpaceID, const nsAtom* aName,
    144                      nsAString& aResult) const {
    145    MOZ_ASSERT(aResult.IsEmpty(), "Should have empty string coming in");
    146    const nsAttrValue* val = GetAttr(aName, aNameSpaceID);
    147    if (!val) {
    148      return false;
    149    }
    150    val->ToString(aResult);
    151    return true;
    152  }
    153 
    154  inline bool GetAttr(const nsAtom* aName, nsAString& aResult) const {
    155    MOZ_ASSERT(aResult.IsEmpty(), "Should have empty string coming in");
    156    const nsAttrValue* val = GetAttr(aName);
    157    if (!val) {
    158      return false;
    159    }
    160    val->ToString(aResult);
    161    return true;
    162  }
    163 
    164  inline bool HasAttr(const nsAtom* aName) const { return !!GetAttr(aName); }
    165 
    166  inline bool HasAttr(int32_t aNameSpaceID, const nsAtom* aName) const {
    167    return !!GetAttr(aName, aNameSpaceID);
    168  }
    169 
    170  inline bool AttrValueIs(int32_t aNameSpaceID, const nsAtom* aName,
    171                          const nsAString& aValue,
    172                          nsCaseTreatment aCaseSensitive) const {
    173    NS_ASSERTION(aName, "Must have attr name");
    174    NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
    175    const nsAttrValue* val = GetAttr(aName, aNameSpaceID);
    176    return val && val->Equals(aValue, aCaseSensitive);
    177  }
    178 
    179  inline bool AttrValueIs(int32_t aNameSpaceID, const nsAtom* aName,
    180                          const nsAtom* aValue,
    181                          nsCaseTreatment aCaseSensitive) const {
    182    NS_ASSERTION(aName, "Must have attr name");
    183    NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
    184    NS_ASSERTION(aValue, "Null value atom");
    185 
    186    const nsAttrValue* val = GetAttr(aName, aNameSpaceID);
    187    return val && val->Equals(aValue, aCaseSensitive);
    188  }
    189 
    190  struct InternalAttr {
    191    nsAttrName mName;
    192    nsAttrValue mValue;
    193  };
    194 
    195  AttrArray(const AttrArray& aOther) = delete;
    196  AttrArray& operator=(const AttrArray& aOther) = delete;
    197 
    198  bool GrowBy(uint32_t aGrowSize);
    199  bool GrowTo(uint32_t aCapacity);
    200 
    201  void Clear() {
    202    // If mImpl contains a tagged bloom filter, release it first to prevent
    203    // unique_ptr from trying to delete it as a pointer
    204    if (HasTaggedBloom()) {
    205      mImpl.release();
    206    } else {
    207      mImpl.reset();
    208    }
    209    // Reinitialize to default tagged bloom filter
    210    SetTaggedBloom(0x1ULL);
    211  }
    212 
    213 private:
    214  // Tries to create an attribute, growing the buffer if needed, with the given
    215  // name and value.
    216  //
    217  // The value is moved from the argument.
    218  //
    219  // `Name` can be anything you construct a `nsAttrName` with (either an atom or
    220  // a NodeInfo pointer).
    221  template <typename Name>
    222  nsresult AddNewAttribute(Name*, nsAttrValue&);
    223 
    224  class Impl {
    225   public:
    226    constexpr static size_t AllocationSizeForAttributes(uint32_t aAttrCount) {
    227      return sizeof(Impl) + aAttrCount * sizeof(InternalAttr);
    228    }
    229 
    230    mozilla::StyleLockedDeclarationBlock* GetMappedDeclarationBlock() const {
    231      return reinterpret_cast<mozilla::StyleLockedDeclarationBlock*>(
    232          mMappedAttributeBits & ~uintptr_t(1));
    233    }
    234 
    235    auto Attrs() const {
    236      return mozilla::Span<const InternalAttr>{mBuffer, mAttrCount};
    237    }
    238 
    239    auto Attrs() { return mozilla::Span<InternalAttr>{mBuffer, mAttrCount}; }
    240 
    241    Impl(const Impl&) = delete;
    242    Impl(Impl&&) = delete;
    243    ~Impl();
    244 
    245    uint32_t mAttrCount;
    246    uint32_t mCapacity;  // In number of InternalAttrs
    247 
    248    // mMappedAttributeBits is a tagged pointer of a
    249    // StyleLockedDeclarationBlock, which holds the style information that our
    250    // attributes map to.
    251    //
    252    // If the lower bit is set, then our mapped attributes are dirty. This just
    253    // means that we might have mapped attributes (or used to and no longer
    254    // have), and are pending an update to recompute our declaration.
    255    uintptr_t mMappedAttributeBits = 0;
    256 
    257    // Combined bloom filter (63 bits) for both classes and attributes
    258    //   Bit 0: Always 1 to match tagged pointer implementation.
    259    //   Bits 1-63: Combined bloom filter (63 bits)
    260    uint64_t mSubtreeBloomFilter;
    261 
    262   public:
    263    Impl() : mSubtreeBloomFilter(0xFFFFFFFFFFFFFFFFULL) {}
    264 
    265    // Allocated in the same buffer as `Impl`.
    266    InternalAttr mBuffer[0];
    267  };
    268 
    269  mozilla::Span<InternalAttr> Attrs() {
    270    return HasImpl() ? GetImpl()->Attrs() : mozilla::Span<InternalAttr>();
    271  }
    272 
    273  mozilla::Span<const InternalAttr> Attrs() const {
    274    return HasImpl() ? GetImpl()->Attrs() : mozilla::Span<const InternalAttr>();
    275  }
    276 
    277  bool HasTaggedBloom() const {
    278    return (reinterpret_cast<uintptr_t>(mImpl.get()) & 1) != 0;
    279  }
    280 
    281  bool HasImpl() const {
    282    MOZ_ASSERT(mImpl.get() != nullptr);
    283    return !HasTaggedBloom();
    284  }
    285 
    286  Impl* GetImpl() {
    287    MOZ_ASSERT(HasImpl());
    288    return mImpl.get();
    289  }
    290 
    291  const Impl* GetImpl() const {
    292    MOZ_ASSERT(HasImpl());
    293    return mImpl.get();
    294  }
    295 
    296  uint64_t GetTaggedBloom() const {
    297    MOZ_ASSERT(HasTaggedBloom());
    298    return reinterpret_cast<uint64_t>(mImpl.get());
    299  }
    300 
    301  void SetTaggedBloom(uint64_t aBloom) {
    302    // If we already have a tagged bloom, release it first
    303    if (HasTaggedBloom()) {
    304      mImpl.release();
    305    }
    306    // Ensure bit 0 is set (tag bit) and upper bit is clear (valid pointer
    307    // range)
    308    MOZ_ASSERT((aBloom & 1) != 0);
    309    mImpl.reset(reinterpret_cast<Impl*>(static_cast<uintptr_t>(aBloom)));
    310  }
    311 
    312  void SetImpl(Impl* aImpl) {
    313    MOZ_ASSERT(aImpl != nullptr &&
    314               (reinterpret_cast<uintptr_t>(aImpl) & 1) == 0);
    315    // If we currently have a tagged bloom filter, release it first to prevent
    316    // unique_ptr from trying to delete it as a pointer
    317    if (HasTaggedBloom()) {
    318      mImpl.release();
    319    }
    320    mImpl.reset(aImpl);
    321  }
    322 
    323 public:
    324  // Set bloom filter directly from 64-bit value
    325  void SetSubtreeBloomFilter(uint64_t aBloom) {
    326    if (HasImpl()) {
    327      GetImpl()->mSubtreeBloomFilter = aBloom;
    328    } else {
    329      SetTaggedBloom(aBloom);
    330    }
    331  }
    332 
    333  // Get bloom filter, used for fast querySelector
    334  uint64_t GetSubtreeBloomFilter() const {
    335    if (HasImpl()) {
    336      return GetImpl()->mSubtreeBloomFilter;
    337    } else if (HasTaggedBloom()) {
    338      return GetTaggedBloom();
    339    }
    340    MOZ_ASSERT_UNREACHABLE("Bloom filter should never be nullptr");
    341    return 0xFFFFFFFFFFFFFFFFULL;
    342  }
    343 
    344  // Update bloom filter with new 64-bit hash
    345  void UpdateSubtreeBloomFilter(uint64_t aHash) {
    346    if (HasImpl()) {
    347      GetImpl()->mSubtreeBloomFilter |= aHash;
    348    } else {
    349      uint64_t current = GetSubtreeBloomFilter();
    350      SetTaggedBloom(current | aHash);
    351    }
    352  }
    353 
    354  // Check if bloom may contain the given hash
    355  bool BloomMayHave(uint64_t aHash) const {
    356    uint64_t bloom = GetSubtreeBloomFilter();
    357    return (bloom & aHash) == aHash;
    358  }
    359 
    360 private:
    361  // Internal method that swaps the current attribute value with aValue.
    362  // Does NOT update bloom filters - external code should always use
    363  // Element::SetAndSwapAttr instead, which calls this and updates bloom
    364  // filters. If the attribute was unset, an empty value will be swapped into
    365  // aValue and aHadValue will be set to false. Otherwise, aHadValue will be set
    366  // to true.
    367  nsresult SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
    368                          bool* aHadValue);
    369  nsresult SetAndSwapAttr(mozilla::dom::NodeInfo* aName, nsAttrValue& aValue,
    370                          bool* aHadValue);
    371 
    372  // mImpl serves dual purposes using pointer tagging:
    373  //
    374  // 1. When the element has no attributes:
    375  //    - Stores a "tagged bloom filter" (bit 0 is 1). The remaining
    376  //      bits are used as a bloomfilter to identify the existence of
    377  //      attribute & class names in this subtree for querySelector.
    378  //
    379  // 2. When the element has attributes:
    380  //    - Points to an Impl struct (bit 0 is 0).
    381  //    - Contains actual attribute storage and the bloom filter
    382  //      is now copied as a member of Impl.
    383  //
    384  // Use HasTaggedBloom() vs HasImpl() to distinguish.
    385  mozilla::BindgenUniquePtr<Impl> mImpl;
    386 };
    387 
    388 #endif