nsIMutationObserver.h (20922B)
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 nsIMutationObserver_h 8 #define nsIMutationObserver_h 9 10 #include <ostream> 11 12 #include "mozilla/Assertions.h" 13 #include "mozilla/DbgMacro.h" 14 #include "mozilla/DoublyLinkedList.h" 15 #include "nsISupports.h" 16 17 class nsAttrValue; 18 class nsAtom; 19 class nsIContent; 20 class nsINode; 21 struct BatchRemovalState; 22 23 namespace mozilla::dom { 24 class Element; 25 } // namespace mozilla::dom 26 27 #define NS_IMUTATION_OBSERVER_IID \ 28 {0x6d674c17, 0x0fbc, 0x4633, {0x8f, 0x46, 0x73, 0x4e, 0x87, 0xeb, 0xf0, 0xc7}} 29 30 /** 31 * Used for Trusted Types' Enforcement for scripts. 32 * https://w3c.github.io/trusted-types/dist/spec/#enforcement-in-scripts 33 */ 34 enum class MutationEffectOnScript : bool { 35 KeepTrustWorthiness, 36 DropTrustWorthiness, 37 }; 38 39 inline std::ostream& operator<<( 40 std::ostream& aStream, MutationEffectOnScript aMutationEffectOnScript) { 41 return aStream << (static_cast<bool>(aMutationEffectOnScript) 42 ? "DropTrustWorthiness" 43 : "KeepTrustWorthiness"); 44 } 45 46 // AttrModType is same as the legacy MutationEvent.attrChange value. 47 enum class AttrModType : uint8_t { 48 Modification = 1, 49 Addition = 2, 50 Removal = 3, 51 }; 52 53 [[nodiscard]] inline bool IsAdditionOrModification(AttrModType aModType) { 54 return aModType == AttrModType::Modification || 55 aModType == AttrModType::Addition; 56 } 57 [[nodiscard]] inline bool IsAdditionOrRemoval(AttrModType aModType) { 58 return aModType == AttrModType::Addition || aModType == AttrModType::Removal; 59 } 60 61 /** 62 * Information details about a characterdata change. Basically, we 63 * view all changes as replacements of a length of text at some offset 64 * with some other text (of possibly some other length). 65 */ 66 struct CharacterDataChangeInfo { 67 /** 68 * True if this character data change is just an append. 69 */ 70 bool mAppend; 71 72 /** 73 * The offset in the text where the change occurred. 74 */ 75 uint32_t mChangeStart; 76 77 /** 78 * The offset such that mChangeEnd - mChangeStart is equal to the length of 79 * the text we removed. If this was a pure insert, append or a result of 80 * `splitText()` this is equal to mChangeStart. 81 */ 82 uint32_t mChangeEnd; 83 84 uint32_t LengthOfRemovedText() const { 85 MOZ_ASSERT(mChangeStart <= mChangeEnd); 86 87 return mChangeEnd - mChangeStart; 88 } 89 90 /** 91 * The length of the text that was inserted in place of the removed text. If 92 * this was a pure text removal, this is 0. 93 */ 94 uint32_t mReplaceLength; 95 96 /** 97 * The net result is that mChangeStart characters at the beginning of the 98 * text remained as they were. The next mChangeEnd - mChangeStart characters 99 * were removed, and mReplaceLength characters were inserted in their place. 100 * The text that used to begin at mChangeEnd now begins at 101 * mChangeStart + mReplaceLength. 102 */ 103 104 MutationEffectOnScript mMutationEffectOnScript = 105 MutationEffectOnScript::DropTrustWorthiness; 106 107 struct MOZ_STACK_CLASS Details { 108 enum { 109 eMerge, // two text nodes are merged as a result of normalize() 110 eSplit // a text node is split as a result of splitText() 111 } mType; 112 /** 113 * For eMerge it's the text node that will be removed, for eSplit it's the 114 * new text node. 115 */ 116 nsIContent* MOZ_NON_OWNING_REF mNextSibling; 117 }; 118 119 /** 120 * Used for splitText() and normalize(), otherwise null. 121 */ 122 Details* mDetails = nullptr; 123 124 MOZ_DEFINE_DBG(CharacterDataChangeInfo, mAppend, mChangeStart, mChangeEnd, 125 mReplaceLength, mMutationEffectOnScript, mDetails); 126 }; 127 128 /** 129 * Information details about a content appending. 130 */ 131 struct ContentAppendInfo { 132 MutationEffectOnScript mMutationEffectOnScript = 133 MutationEffectOnScript::DropTrustWorthiness; 134 nsINode* mOldParent = nullptr; 135 }; 136 137 /** 138 * Information details about a content insertion. 139 */ 140 using ContentInsertInfo = ContentAppendInfo; 141 142 /** 143 * Information details about a content removal. 144 */ 145 struct ContentRemoveInfo { 146 /* Whether we'll be removing all children of this 147 container. This is useful to avoid wasteful work. */ 148 const BatchRemovalState* mBatchRemovalState = nullptr; 149 150 MutationEffectOnScript mMutationEffectOnScript = 151 MutationEffectOnScript::DropTrustWorthiness; 152 nsINode* mNewParent = nullptr; 153 }; 154 155 /** 156 * Mutation observer interface 157 * 158 * See nsINode::AddMutationObserver, nsINode::RemoveMutationObserver for how to 159 * attach or remove your observers. nsINode stores mutation observers using a 160 * mozilla::SafeDoublyLinkedList, which is a specialization of the 161 * DoublyLinkedList allowing for adding/removing elements while iterating. 162 * If a mutation observer is intended to be added to multiple nsINode instances, 163 * derive from nsMultiMutationObserver. 164 * 165 * WARNING: During these notifications, you are not allowed to perform 166 * any mutations to the current or any other document, or start a 167 * network load. If you need to perform such operations do that 168 * during the _last_ nsIDocumentObserver::EndUpdate notification. The 169 * exception for this is ParentChainChanged, where mutations should be 170 * done from an async event, as the notification might not be 171 * surrounded by BeginUpdate/EndUpdate calls. 172 */ 173 class nsIMutationObserver 174 : public nsISupports, 175 mozilla::DoublyLinkedListElement<nsIMutationObserver> { 176 friend struct mozilla::GetDoublyLinkedListElement<nsIMutationObserver>; 177 178 public: 179 NS_INLINE_DECL_STATIC_IID(NS_IMUTATION_OBSERVER_IID) 180 181 /** 182 * Notification that the node value of a data node (text, cdata, pi, comment) 183 * will be changed. 184 * 185 * This notification is not sent when a piece of content is 186 * added/removed from the document (the other notifications are used 187 * for that). 188 * 189 * @param aContent The piece of content that changed. Is never null. 190 * @param aInfo The structure with information details about the change. 191 * 192 * @note Callers of this method might not hold a strong reference to the 193 * observer. The observer is responsible for making sure it stays 194 * alive for the duration of the call as needed. The observer may 195 * assume that this call will happen when there are script blockers on 196 * the stack. 197 */ 198 virtual void CharacterDataWillChange(nsIContent* aContent, 199 const CharacterDataChangeInfo&) = 0; 200 201 /** 202 * Notification that the node value of a data node (text, cdata, pi, comment) 203 * has changed. 204 * 205 * This notification is not sent when a piece of content is 206 * added/removed from the document (the other notifications are used 207 * for that). 208 * 209 * @param aContent The piece of content that changed. Is never null. 210 * @param aInfo The structure with information details about the change. 211 * 212 * @note Callers of this method might not hold a strong reference to the 213 * observer. The observer is responsible for making sure it stays 214 * alive for the duration of the call as needed. The observer may 215 * assume that this call will happen when there are script blockers on 216 * the stack. 217 */ 218 virtual void CharacterDataChanged(nsIContent* aContent, 219 const CharacterDataChangeInfo&) = 0; 220 221 /** 222 * Notification that an attribute of an element will change. This 223 * can happen before the BeginUpdate for the change and may not 224 * always be followed by an AttributeChanged (in particular, if the 225 * attribute doesn't actually change there will be no corresponding 226 * AttributeChanged). 227 * 228 * @param aContent The element whose attribute will change 229 * @param aNameSpaceID The namespace id of the changing attribute 230 * @param aAttribute The name of the changing attribute 231 * @param aModType Whether or not the attribute will be added, changed, or 232 * removed. 233 * 234 * @note Callers of this method might not hold a strong reference to the 235 * observer. The observer is responsible for making sure it stays 236 * alive for the duration of the call as needed. The observer may 237 * assume that this call will happen when there are script blockers on 238 * the stack. 239 */ 240 virtual void AttributeWillChange(mozilla::dom::Element* aElement, 241 int32_t aNameSpaceID, nsAtom* aAttribute, 242 AttrModType aModType) = 0; 243 244 /** 245 * Notification that an attribute of an element has changed. 246 * 247 * @param aElement The element whose attribute changed 248 * @param aNameSpaceID The namespace id of the changed attribute 249 * @param aAttribute The name of the changed attribute 250 * @param aModType Whether or not the attribute was added, changed, or 251 * removed. 252 * @param aOldValue The old value, if either the old value or the new 253 * value are StoresOwnData() (or absent); null otherwise. 254 * 255 * @note Callers of this method might not hold a strong reference to the 256 * observer. The observer is responsible for making sure it stays 257 * alive for the duration of the call as needed. The observer may 258 * assume that this call will happen when there are script blockers on 259 * the stack. 260 */ 261 virtual void AttributeChanged(mozilla::dom::Element* aElement, 262 int32_t aNameSpaceID, nsAtom* aAttribute, 263 AttrModType aModType, 264 const nsAttrValue* aOldValue) = 0; 265 266 /** 267 * Notification that an attribute of an element has been 268 * set to the value it already had. 269 * 270 * @param aElement The element whose attribute changed 271 * @param aNameSpaceID The namespace id of the changed attribute 272 * @param aAttribute The name of the changed attribute 273 */ 274 virtual void AttributeSetToCurrentValue(mozilla::dom::Element* aElement, 275 int32_t aNameSpaceID, 276 nsAtom* aAttribute) {} 277 278 /** 279 * Notification that one or more content nodes have been appended to the 280 * child list of another node in the tree. 281 * 282 * @param aFirstNewContent the first node appended. 283 * @param aInfo The structure with information details about the change. 284 * 285 * @note Callers of this method might not hold a strong reference to the 286 * observer. The observer is responsible for making sure it stays 287 * alive for the duration of the call as needed. The observer may 288 * assume that this call will happen when there are script blockers on 289 * the stack. 290 */ 291 virtual void ContentAppended(nsIContent* aFirstNewContent, 292 const ContentAppendInfo&) = 0; 293 294 /** 295 * Notification that a content node has been inserted as child to another 296 * node in the tree. 297 * 298 * @param aChild The newly inserted child. 299 * @param aInfo The structure with information details about the change. 300 * 301 * @note Callers of this method might not hold a strong reference to the 302 * observer. The observer is responsible for making sure it stays 303 * alive for the duration of the call as needed. The observer may 304 * assume that this call will happen when there are script blockers on 305 * the stack. 306 */ 307 virtual void ContentInserted(nsIContent* aChild, 308 const ContentInsertInfo&) = 0; 309 310 /** 311 * Notification that a content node is about to be removed from the child list 312 * of another node in the tree. 313 * 314 * @param aChild The child that will be removed. 315 * @param aState The state of our batch removal of all children, or null. 316 * @param aInfo The structure with information details about the change. 317 * 318 * @note Callers of this method might not hold a strong reference to the 319 * observer. The observer is responsible for making sure it stays 320 * alive for the duration of the call as needed. The observer may 321 * assume that this call will happen when there are script blockers on 322 * the stack. 323 */ 324 virtual void ContentWillBeRemoved(nsIContent* aChild, 325 const ContentRemoveInfo&) = 0; 326 327 /** 328 * The node is in the process of being destroyed. Calling QI on the node is 329 * not supported, however it is possible to get children and flags through 330 * nsINode as well as calling IsContent and casting to nsIContent to get 331 * attributes. 332 * 333 * NOTE: This notification is only called on observers registered directly 334 * on the node. This is because when the node is destroyed it can not have 335 * any ancestors. If you want to know when a descendant node is being 336 * removed from the observed node, use the ContentWillBeRemoved notification. 337 * 338 * @param aNode The node being destroyed. 339 * 340 * @note Callers of this method might not hold a strong reference to 341 * the observer. The observer is responsible for making sure it 342 * stays alive for the duration of the call as needed. 343 */ 344 virtual void NodeWillBeDestroyed(nsINode* aNode) = 0; 345 346 /** 347 * Notification that the node's parent chain has changed. This 348 * happens when either the node or one of its ancestors is inserted 349 * or removed as a child of another node. 350 * 351 * Note that when a node is inserted this notification is sent to 352 * all descendants of that node, since all such nodes have their 353 * parent chain changed. 354 * 355 * @param aContent The piece of content that had its parent changed. 356 * 357 * @note Callers of this method might not hold a strong reference to 358 * the observer. The observer is responsible for making sure it 359 * stays alive for the duration of the call as needed. 360 */ 361 362 virtual void ParentChainChanged(nsIContent* aContent) = 0; 363 364 virtual void ARIAAttributeDefaultWillChange(mozilla::dom::Element* aElement, 365 nsAtom* aAttribute, 366 AttrModType aModType) = 0; 367 virtual void ARIAAttributeDefaultChanged(mozilla::dom::Element* aElement, 368 nsAtom* aAttribute, 369 AttrModType aModType) = 0; 370 371 enum : uint32_t { 372 kNone = 0, 373 kCharacterDataWillChange = 1 << 0, 374 kCharacterDataChanged = 1 << 1, 375 kAttributeWillChange = 1 << 2, 376 kAttributeChanged = 1 << 3, 377 kAttributeSetToCurrentValue = 1 << 4, 378 kContentAppended = 1 << 5, 379 kContentInserted = 1 << 6, 380 kContentWillBeRemoved = 1 << 7, 381 kNodeWillBeDestroyed = 1 << 8, 382 kParentChainChanged = 1 << 9, 383 kARIAAttributeDefaultWillChange = 1 << 10, 384 kARIAAttributeDefaultChanged = 1 << 11, 385 386 kBeginUpdate = 1 << 12, 387 kEndUpdate = 1 << 13, 388 kBeginLoad = 1 << 14, 389 kEndLoad = 1 << 15, 390 kElementStateChanged = 1 << 16, 391 392 kAnimationAdded = 1 << 17, 393 kAnimationChanged = 1 << 18, 394 kAnimationRemoved = 1 << 19, 395 396 kAll = 0xFFFFFFFF 397 }; 398 399 void SetEnabledCallbacks(uint32_t aCallbacks) { 400 mEnabledCallbacks = aCallbacks; 401 } 402 403 bool IsCallbackEnabled(uint32_t aCallback) const { 404 return mEnabledCallbacks & aCallback; 405 } 406 407 private: 408 uint32_t mEnabledCallbacks = kAll; 409 }; 410 411 #define NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE \ 412 virtual void CharacterDataWillChange( \ 413 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) override; 414 415 #define NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED \ 416 virtual void CharacterDataChanged( \ 417 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) override; 418 419 #define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE \ 420 virtual void AttributeWillChange(mozilla::dom::Element* aElement, \ 421 int32_t aNameSpaceID, nsAtom* aAttribute, \ 422 AttrModType aModType) override; 423 424 #define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \ 425 virtual void AttributeChanged(mozilla::dom::Element* aElement, \ 426 int32_t aNameSpaceID, nsAtom* aAttribute, \ 427 AttrModType aModType, \ 428 const nsAttrValue* aOldValue) override; 429 430 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED \ 431 virtual void ContentAppended(nsIContent* aFirstNewContent, \ 432 const ContentAppendInfo&) override; 433 434 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED \ 435 virtual void ContentInserted(nsIContent* aChild, const ContentInsertInfo&) \ 436 override; 437 438 #define NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED \ 439 virtual void ContentWillBeRemoved(nsIContent* aChild, \ 440 const ContentRemoveInfo&) override; 441 442 #define NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED \ 443 virtual void NodeWillBeDestroyed(nsINode* aNode) override; 444 445 #define NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED \ 446 virtual void ParentChainChanged(nsIContent* aContent) override; 447 448 #define NS_DECL_NSIMUTATIONOBSERVER_ARIAATTRIBUTEDEFAULTWILLCHANGE \ 449 virtual void ARIAAttributeDefaultWillChange(mozilla::dom::Element* aElement, \ 450 nsAtom* aAttribute, \ 451 AttrModType aModType) override; 452 453 #define NS_DECL_NSIMUTATIONOBSERVER_ARIAATTRIBUTEDEFAULTCHANGED \ 454 virtual void ARIAAttributeDefaultChanged(mozilla::dom::Element* aElement, \ 455 nsAtom* aAttribute, \ 456 AttrModType aModType) override; 457 458 #define NS_DECL_NSIMUTATIONOBSERVER \ 459 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE \ 460 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED \ 461 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE \ 462 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \ 463 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED \ 464 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED \ 465 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED \ 466 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED \ 467 NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED \ 468 NS_DECL_NSIMUTATIONOBSERVER_ARIAATTRIBUTEDEFAULTWILLCHANGE \ 469 NS_DECL_NSIMUTATIONOBSERVER_ARIAATTRIBUTEDEFAULTCHANGED 470 471 #define NS_IMPL_NSIMUTATIONOBSERVER_CORE_STUB(_class) \ 472 void _class::NodeWillBeDestroyed(nsINode* aNode) {} 473 474 #define NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class) \ 475 void _class::CharacterDataWillChange( \ 476 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {} \ 477 void _class::CharacterDataChanged(nsIContent* aContent, \ 478 const CharacterDataChangeInfo& aInfo) {} \ 479 void _class::AttributeWillChange(mozilla::dom::Element* aElement, \ 480 int32_t aNameSpaceID, nsAtom* aAttribute, \ 481 AttrModType aModType) {} \ 482 void _class::AttributeChanged(mozilla::dom::Element* aElement, \ 483 int32_t aNameSpaceID, nsAtom* aAttribute, \ 484 AttrModType aModType, \ 485 const nsAttrValue* aOldValue) {} \ 486 void _class::ContentAppended(nsIContent* aFirstNewContent, \ 487 const ContentAppendInfo&) {} \ 488 void _class::ContentInserted(nsIContent* aChild, const ContentInsertInfo&) { \ 489 } \ 490 void _class::ContentWillBeRemoved(nsIContent* aChild, \ 491 const ContentRemoveInfo&) {} \ 492 void _class::ParentChainChanged(nsIContent* aContent) {} \ 493 void _class::ARIAAttributeDefaultWillChange(mozilla::dom::Element* aElement, \ 494 nsAtom* aAttribute, \ 495 AttrModType aModType) {} \ 496 void _class::ARIAAttributeDefaultChanged(mozilla::dom::Element* aElement, \ 497 nsAtom* aAttribute, \ 498 AttrModType aModType) {} 499 500 #endif /* nsIMutationObserver_h */