Bug 1728955: part 3) Add logging to `nsBaseClipboard`. r=masayuki
[gecko.git] / dom / base / MutationObservers.cpp
blobd2f4afcd77d98ad91b27a8c0e6ba8238cd4e9698
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/. */
7 #include "MutationObservers.h"
9 #include "nsContentUtils.h"
10 #include "nsCSSPseudoElements.h"
11 #include "nsINode.h"
12 #include "nsIContent.h"
13 #include "nsIContentInlines.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/DocumentInlines.h"
16 #include "mozilla/dom/Element.h"
17 #include "nsIMutationObserver.h"
18 #include "mozilla/EventListenerManager.h"
19 #include "PLDHashTable.h"
20 #include "nsCOMArray.h"
21 #include "nsPIDOMWindow.h"
22 #ifdef MOZ_XUL
23 # include "nsXULElement.h"
24 #endif
25 #include "nsGenericHTMLElement.h"
26 #include "mozilla/AnimationTarget.h"
27 #include "mozilla/Assertions.h"
28 #include "mozilla/ErrorResult.h"
29 #include "mozilla/dom/Animation.h"
30 #include "mozilla/dom/KeyframeEffect.h"
31 #include "mozilla/PresShell.h"
32 #include "nsWrapperCacheInlines.h"
33 #include "nsDOMMutationObserver.h"
34 #include "mozilla/dom/BindingUtils.h"
35 #include "mozilla/dom/CustomElementRegistry.h"
36 #include "mozilla/dom/HTMLTemplateElement.h"
37 #include "mozilla/dom/ShadowRoot.h"
39 using namespace mozilla;
40 using namespace mozilla::dom;
42 #define NOTIFY_PRESSHELL(notify_) \
43 if (PresShell* presShell = doc->GetObservingPresShell()) { \
44 notify_(presShell); \
47 #define DEFINE_NOTIFIERS(func_, params_) \
48 auto notifyPresShell = [&](PresShell* aPresShell) { \
49 aPresShell->func_ params_; \
50 }; \
51 auto notifyObserver = [&](nsIMutationObserver* aObserver) { \
52 aObserver->func_ params_; \
55 template <typename NotifyObserver>
56 static inline nsINode* ForEachAncestorObserver(nsINode* aNode,
57 NotifyObserver& aFunc) {
58 nsINode* last;
59 nsINode* node = aNode;
60 do {
61 nsAutoTObserverArray<nsIMutationObserver*, 1>* observers =
62 node->GetMutationObservers();
63 if (observers && !observers->IsEmpty()) {
64 for (nsIMutationObserver* obs : observers->ForwardRange()) {
65 aFunc(obs);
68 last = node;
69 if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
70 node = shadow->GetHost();
71 } else {
72 node = node->GetParentNode();
74 } while (node);
75 return last;
78 enum class IsRemoval { No, Yes };
79 enum class ShouldAssert { No, Yes };
81 template <IsRemoval aIsRemoval = IsRemoval::No,
82 ShouldAssert aShouldAssert = ShouldAssert::Yes,
83 typename NotifyPresShell, typename NotifyObserver>
84 static inline void Notify(nsINode* aNode, NotifyPresShell& aNotifyPresShell,
85 NotifyObserver& aNotifyObserver) {
86 Document* doc = aNode->OwnerDoc();
87 nsDOMMutationEnterLeave enterLeave(doc);
89 const bool wasConnected = aNode->IsInComposedDoc();
90 // For removals, the pres shell gets notified first, since it needs to operate
91 // on the "old" DOM shape.
92 if (aIsRemoval == IsRemoval::Yes && wasConnected) {
93 NOTIFY_PRESSHELL(aNotifyPresShell);
95 nsINode* last = ForEachAncestorObserver(aNode, aNotifyObserver);
96 // For non-removals, the pres shell gets notified last, since it needs to
97 // operate on the "final" DOM shape.
98 if (aIsRemoval == IsRemoval::No && last == doc) {
99 NOTIFY_PRESSHELL(aNotifyPresShell);
101 if (aShouldAssert == ShouldAssert::Yes) {
102 MOZ_ASSERT((last == doc) == wasConnected,
103 "If we were connected we should notify all ancestors all the "
104 "way to the document");
108 #define IMPL_ANIMATION_NOTIFICATION(func_, content_, params_) \
109 PR_BEGIN_MACRO \
110 nsDOMMutationEnterLeave enterLeave(doc); \
111 auto forEach = [&](nsIMutationObserver* aObserver) { \
112 if (nsCOMPtr<nsIAnimationObserver> obs = do_QueryInterface(aObserver)) { \
113 obs->func_ params_; \
115 }; \
116 ForEachAncestorObserver(content_, forEach); \
117 PR_END_MACRO
119 namespace mozilla {
120 void MutationObservers::NotifyCharacterDataWillChange(
121 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
122 DEFINE_NOTIFIERS(CharacterDataWillChange, (aContent, aInfo));
123 Notify(aContent, notifyPresShell, notifyObserver);
126 void MutationObservers::NotifyCharacterDataChanged(
127 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
128 aContent->OwnerDoc()->Changed();
129 DEFINE_NOTIFIERS(CharacterDataChanged, (aContent, aInfo));
130 Notify(aContent, notifyPresShell, notifyObserver);
133 void MutationObservers::NotifyAttributeWillChange(Element* aElement,
134 int32_t aNameSpaceID,
135 nsAtom* aAttribute,
136 int32_t aModType) {
137 DEFINE_NOTIFIERS(AttributeWillChange,
138 (aElement, aNameSpaceID, aAttribute, aModType));
139 Notify(aElement, notifyPresShell, notifyObserver);
142 void MutationObservers::NotifyAttributeChanged(Element* aElement,
143 int32_t aNameSpaceID,
144 nsAtom* aAttribute,
145 int32_t aModType,
146 const nsAttrValue* aOldValue) {
147 aElement->OwnerDoc()->Changed();
148 DEFINE_NOTIFIERS(AttributeChanged,
149 (aElement, aNameSpaceID, aAttribute, aModType, aOldValue));
150 Notify(aElement, notifyPresShell, notifyObserver);
153 void MutationObservers::NotifyAttributeSetToCurrentValue(Element* aElement,
154 int32_t aNameSpaceID,
155 nsAtom* aAttribute) {
156 DEFINE_NOTIFIERS(AttributeSetToCurrentValue,
157 (aElement, aNameSpaceID, aAttribute));
158 Notify(aElement, notifyPresShell, notifyObserver);
161 void MutationObservers::NotifyContentAppended(nsIContent* aContainer,
162 nsIContent* aFirstNewContent) {
163 aContainer->OwnerDoc()->Changed();
164 DEFINE_NOTIFIERS(ContentAppended, (aFirstNewContent));
165 Notify(aContainer, notifyPresShell, notifyObserver);
168 void MutationObservers::NotifyNativeAnonymousChildListChange(
169 nsIContent* aContent, bool aIsRemove) {
170 DEFINE_NOTIFIERS(NativeAnonymousChildListChange, (aContent, aIsRemove));
171 if (aIsRemove) {
172 // We can't actually assert that we reach the document if we're connected,
173 // since this notification runs from UnbindFromTree.
174 Notify<IsRemoval::Yes, ShouldAssert::No>(aContent, notifyPresShell,
175 notifyObserver);
176 } else {
177 Notify(aContent, notifyPresShell, notifyObserver);
181 void MutationObservers::NotifyContentInserted(nsINode* aContainer,
182 nsIContent* aChild) {
183 MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
184 "container must be an nsIContent or an Document");
185 aContainer->OwnerDoc()->Changed();
186 DEFINE_NOTIFIERS(ContentInserted, (aChild));
187 Notify(aContainer, notifyPresShell, notifyObserver);
190 void MutationObservers::NotifyContentRemoved(nsINode* aContainer,
191 nsIContent* aChild,
192 nsIContent* aPreviousSibling) {
193 MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
194 "container must be an nsIContent or an Document");
195 aContainer->OwnerDoc()->Changed();
196 MOZ_ASSERT(aChild->GetParentNode() == aContainer,
197 "We expect the parent link to be still around at this point");
198 DEFINE_NOTIFIERS(ContentRemoved, (aChild, aPreviousSibling));
199 Notify<IsRemoval::Yes>(aContainer, notifyPresShell, notifyObserver);
202 } // namespace mozilla
204 void MutationObservers::NotifyAnimationMutated(
205 dom::Animation* aAnimation, AnimationMutationType aMutatedType) {
206 MOZ_ASSERT(aAnimation);
208 NonOwningAnimationTarget target = aAnimation->GetTargetForAnimation();
209 if (!target) {
210 return;
213 // A pseudo element and its parent element use the same owner doc.
214 Document* doc = target.mElement->OwnerDoc();
215 if (doc->MayHaveAnimationObservers()) {
216 // we use the its parent element as the subject in DOM Mutation Observer.
217 Element* elem = target.mElement;
218 switch (aMutatedType) {
219 case AnimationMutationType::Added:
220 IMPL_ANIMATION_NOTIFICATION(AnimationAdded, elem, (aAnimation));
221 break;
222 case AnimationMutationType::Changed:
223 IMPL_ANIMATION_NOTIFICATION(AnimationChanged, elem, (aAnimation));
224 break;
225 case AnimationMutationType::Removed:
226 IMPL_ANIMATION_NOTIFICATION(AnimationRemoved, elem, (aAnimation));
227 break;
228 default:
229 MOZ_ASSERT_UNREACHABLE("unexpected mutation type");
234 void MutationObservers::NotifyAnimationAdded(dom::Animation* aAnimation) {
235 NotifyAnimationMutated(aAnimation, AnimationMutationType::Added);
238 void MutationObservers::NotifyAnimationChanged(dom::Animation* aAnimation) {
239 NotifyAnimationMutated(aAnimation, AnimationMutationType::Changed);
242 void MutationObservers::NotifyAnimationRemoved(dom::Animation* aAnimation) {
243 NotifyAnimationMutated(aAnimation, AnimationMutationType::Removed);