IdentifierMapEntry.h (8018B)
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 * Entry for the Document or ShadowRoot's identifier map. 9 */ 10 11 #ifndef mozilla_IdentifierMapEntry_h 12 #define mozilla_IdentifierMapEntry_h 13 14 #include <utility> 15 16 #include "PLDHashTable.h" 17 #include "mozilla/MemoryReporting.h" 18 #include "mozilla/UniquePtr.h" 19 #include "mozilla/dom/TreeOrderedArray.h" 20 #include "nsAtom.h" 21 #include "nsCOMPtr.h" 22 #include "nsContentList.h" 23 #include "nsHashKeys.h" 24 #include "nsTArray.h" 25 #include "nsTHashtable.h" 26 27 class nsIContent; 28 class nsINode; 29 30 namespace mozilla { 31 namespace dom { 32 class Document; 33 class Element; 34 } // namespace dom 35 36 /** 37 * Right now our identifier map entries contain information for 'name' 38 * and 'id' mappings of a given string. This is so that 39 * nsHTMLDocument::ResolveName only has to do one hash lookup instead 40 * of two. It's not clear whether this still matters for performance. 41 * 42 * We also store the document.all result list here. This is mainly so that 43 * when all elements with the given ID are removed and we remove 44 * the ID's IdentifierMapEntry, the document.all result is released too. 45 * Perhaps the document.all results should have their own hashtable 46 * in nsHTMLDocument. 47 */ 48 class IdentifierMapEntry : public PLDHashEntryHdr { 49 typedef dom::Document Document; 50 typedef dom::Element Element; 51 52 /** 53 * @see Document::IDTargetObserver, this is just here to avoid include hell. 54 */ 55 typedef bool (*IDTargetObserver)(Element* aOldElement, Element* aNewelement, 56 void* aData); 57 58 public: 59 // We use DependentAtomOrString as our external key interface. This allows 60 // consumers to use an nsAString, for example, without forcing a copy. 61 struct DependentAtomOrString final { 62 MOZ_IMPLICIT DependentAtomOrString(nsAtom* aAtom) 63 : mAtom(aAtom), mString(nullptr) {} 64 MOZ_IMPLICIT DependentAtomOrString(const nsAString& aString) 65 : mAtom(nullptr), mString(&aString) {} 66 DependentAtomOrString(const DependentAtomOrString& aOther) = default; 67 68 nsAtom* mAtom; 69 const nsAString* mString; 70 }; 71 72 typedef const DependentAtomOrString& KeyType; 73 typedef const DependentAtomOrString* KeyTypePointer; 74 75 explicit IdentifierMapEntry(const DependentAtomOrString* aKey); 76 IdentifierMapEntry(IdentifierMapEntry&& aOther) = default; 77 ~IdentifierMapEntry() = default; 78 79 nsString GetKeyAsString() const { 80 if (mKey.mAtom) { 81 return nsAtomString(mKey.mAtom); 82 } 83 84 return mKey.mString; 85 } 86 87 bool KeyEquals(const KeyTypePointer aOtherKey) const { 88 if (mKey.mAtom) { 89 if (aOtherKey->mAtom) { 90 return mKey.mAtom == aOtherKey->mAtom; 91 } 92 93 return mKey.mAtom->Equals(*aOtherKey->mString); 94 } 95 96 if (aOtherKey->mAtom) { 97 return aOtherKey->mAtom->Equals(mKey.mString); 98 } 99 100 return mKey.mString.Equals(*aOtherKey->mString); 101 } 102 103 static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } 104 105 static PLDHashNumber HashKey(const KeyTypePointer aKey) { 106 return aKey->mAtom ? aKey->mAtom->hash() : HashString(*aKey->mString); 107 } 108 109 enum { ALLOW_MEMMOVE = false }; 110 111 bool IsEmpty(); 112 113 void AddNameElement(nsINode* aDocument, Element* aElement); 114 void RemoveNameElement(Element* aElement); 115 nsBaseContentList* GetNameContentList() { return mNameContentList; } 116 bool HasNameElement() const; 117 118 void AddDocumentNameElement(Document* aDocument, 119 nsGenericHTMLElement* aElement); 120 void RemoveDocumentNameElement(nsGenericHTMLElement* aElement); 121 bool HasDocumentNameElement() const; 122 nsBaseContentList* GetDocumentNameContentList() { 123 return mDocumentNameContentList; 124 } 125 126 /** 127 * Returns the element if we know the element associated with this 128 * id. Otherwise returns null. 129 */ 130 Element* GetIdElement() const { 131 auto span = mIdContentList.AsSpan(); 132 return span.IsEmpty() ? nullptr : span[0]; 133 } 134 135 /** 136 * Returns the list of all elements associated with this id. 137 */ 138 Span<Element* const> GetIdElements() const { return mIdContentList.AsSpan(); } 139 140 /** 141 * If this entry has a non-null image element set (using SetImageElement), 142 * the image element will be returned, otherwise the same as GetIdElement(). 143 */ 144 Element* GetImageIdElement() { 145 return mImageElement ? mImageElement.get() : GetIdElement(); 146 } 147 148 /** 149 * This can fire ID change callbacks. 150 */ 151 void AddIdElement(Element* aElement); 152 /** 153 * This can fire ID change callbacks. 154 */ 155 void RemoveIdElement(Element* aElement); 156 /** 157 * Set the image element override for this ID. This will be returned by 158 * GetIdElement(true) if non-null. 159 */ 160 void SetImageElement(Element* aElement); 161 bool HasIdElementExposedAsHTMLDocumentProperty() const; 162 163 bool HasContentChangeCallback() { return mChangeCallbacks != nullptr; } 164 void AddContentChangeCallback(IDTargetObserver aCallback, void* aData, 165 bool aForImage); 166 void RemoveContentChangeCallback(IDTargetObserver aCallback, void* aData, 167 bool aForImage); 168 169 /** 170 * Remove all elements and notify change listeners. 171 */ 172 void ClearAndNotify(); 173 174 void Traverse(nsCycleCollectionTraversalCallback* aCallback); 175 176 struct ChangeCallback { 177 IDTargetObserver mCallback; 178 void* mData; 179 bool mForImage; 180 }; 181 182 struct ChangeCallbackEntry : public PLDHashEntryHdr { 183 typedef const ChangeCallback KeyType; 184 typedef const ChangeCallback* KeyTypePointer; 185 186 explicit ChangeCallbackEntry(const ChangeCallback* aKey) : mKey(*aKey) {} 187 ChangeCallbackEntry(ChangeCallbackEntry&& aOther) 188 : PLDHashEntryHdr(std::move(aOther)), mKey(std::move(aOther.mKey)) {} 189 190 KeyType GetKey() const { return mKey; } 191 bool KeyEquals(KeyTypePointer aKey) const { 192 return aKey->mCallback == mKey.mCallback && aKey->mData == mKey.mData && 193 aKey->mForImage == mKey.mForImage; 194 } 195 196 static KeyTypePointer KeyToPointer(KeyType& aKey) { return &aKey; } 197 static PLDHashNumber HashKey(KeyTypePointer aKey) { 198 return HashGeneric(aKey->mCallback, aKey->mData); 199 } 200 enum { ALLOW_MEMMOVE = true }; 201 202 ChangeCallback mKey; 203 }; 204 205 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; 206 207 private: 208 // We use an OwningAtomOrString as our internal key storage. It needs to own 209 // the key string, whether in atom or string form. 210 struct OwningAtomOrString final { 211 OwningAtomOrString(const OwningAtomOrString& aOther) = delete; 212 OwningAtomOrString(OwningAtomOrString&& aOther) = default; 213 214 explicit OwningAtomOrString(const DependentAtomOrString& aOther) 215 // aOther may have a null mString, so jump through a bit of a hoop in 216 // that case. I wish there were a way to just default-initialize 217 // mString in that situation... We could also make mString not const 218 // and only assign to it if aOther.mString is not null, but having it be 219 // const is nice. 220 : mAtom(aOther.mAtom), 221 mString(aOther.mString ? *aOther.mString : u""_ns) {} 222 223 RefPtr<nsAtom> mAtom; 224 nsString mString; 225 }; 226 227 IdentifierMapEntry(const IdentifierMapEntry& aOther) = delete; 228 IdentifierMapEntry& operator=(const IdentifierMapEntry& aOther) = delete; 229 230 void FireChangeCallbacks(Element* aOldElement, Element* aNewElement, 231 bool aImageOnly = false); 232 233 OwningAtomOrString mKey; 234 dom::TreeOrderedArray<Element*> mIdContentList; 235 RefPtr<nsBaseContentList> mNameContentList; 236 // The content list for the document named getter. 237 RefPtr<nsBaseContentList> mDocumentNameContentList; 238 UniquePtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks; 239 RefPtr<Element> mImageElement; 240 }; 241 242 } // namespace mozilla 243 244 #endif // #ifndef mozilla_IdentifierMapEntry_h