Bug 1834103 - Optimize out some nsIMutationObserver calls, r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D178558
This commit is contained in:
Olli Pettay 2023-05-19 21:34:44 +00:00
Родитель 175fec1662
Коммит 80bb4b4f78
8 изменённых файлов: 110 добавлений и 28 удалений

Просмотреть файл

@ -574,7 +574,11 @@ class Document : public nsINode,
#define NS_DOCUMENT_NOTIFY_OBSERVERS(func_, params_) \
do { \
NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers, func_, params_); \
for (RefPtr obs : mObservers.ForwardRange()) { \
if (obs->IsCallbackEnabled(nsIMutationObserver::k##func_)) { \
obs->func_ params_; \
} \
} \
/* FIXME(emilio): Apparently we can keep observing from the BFCache? That \
looks bogus. */ \
if (PresShell* presShell = GetObservingPresShell()) { \

Просмотреть файл

@ -47,22 +47,25 @@ using namespace mozilla::dom;
template <typename NotifyObserver>
static inline nsINode* ForEachAncestorObserver(nsINode* aNode,
NotifyObserver& aFunc) {
NotifyObserver& aFunc,
uint32_t aCallback) {
nsINode* last;
nsINode* node = aNode;
do {
mozilla::SafeDoublyLinkedList<nsIMutationObserver>* observers =
node->GetMutationObservers();
if (observers && !observers->isEmpty()) {
if (observers) {
for (auto iter = observers->begin(); iter != observers->end(); ++iter) {
aFunc(&*iter);
if (iter->IsCallbackEnabled(aCallback)) {
aFunc(&*iter);
}
}
}
last = node;
if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
node = shadow->GetHost();
} else {
node = node->GetParentNode();
if (!(node = node->GetParentNode())) {
if (ShadowRoot* shadow = ShadowRoot::FromNode(last)) {
node = shadow->GetHost();
}
}
} while (node);
return last;
@ -75,7 +78,8 @@ enum class NotifyPresShell { No, Before, After };
template <NotifyPresShell aNotifyPresShell = NotifyPresShell::After,
typename NotifyObserver>
static inline void Notify(nsINode* aNode, NotifyObserver&& aNotify) {
static inline void Notify(nsINode* aNode, NotifyObserver&& aNotify,
uint32_t aCallback) {
Document* doc = aNode->OwnerDoc();
nsDOMMutationEnterLeave enterLeave(doc);
@ -87,7 +91,7 @@ static inline void Notify(nsINode* aNode, NotifyObserver&& aNotify) {
NOTIFY_PRESSHELL(aNotify);
}
}
nsINode* last = ForEachAncestorObserver(aNode, aNotify);
nsINode* last = ForEachAncestorObserver(aNode, aNotify, aCallback);
// For non-removals, the pres shell gets notified last, since it needs to
// operate on the "final" DOM shape.
if constexpr (aNotifyPresShell == NotifyPresShell::After) {
@ -108,27 +112,31 @@ static inline void Notify(nsINode* aNode, NotifyObserver&& aNotify) {
obs->func_ params_; \
} \
}; \
ForEachAncestorObserver(content_, forEach); \
ForEachAncestorObserver(content_, forEach, nsIMutationObserver::k##func_); \
PR_END_MACRO
namespace mozilla {
void MutationObservers::NotifyCharacterDataWillChange(
nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
Notify(aContent, NOTIFIER(CharacterDataWillChange, aContent, aInfo));
Notify(aContent, NOTIFIER(CharacterDataWillChange, aContent, aInfo),
nsIMutationObserver::kCharacterDataWillChange);
}
void MutationObservers::NotifyCharacterDataChanged(
nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
aContent->OwnerDoc()->Changed();
Notify(aContent, NOTIFIER(CharacterDataChanged, aContent, aInfo));
Notify(aContent, NOTIFIER(CharacterDataChanged, aContent, aInfo),
nsIMutationObserver::kCharacterDataChanged);
}
void MutationObservers::NotifyAttributeWillChange(Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) {
Notify(aElement, NOTIFIER(AttributeWillChange, aElement, aNameSpaceID,
aAttribute, aModType));
Notify(aElement,
NOTIFIER(AttributeWillChange, aElement, aNameSpaceID, aAttribute,
aModType),
nsIMutationObserver::kAttributeWillChange);
}
void MutationObservers::NotifyAttributeChanged(Element* aElement,
@ -137,21 +145,26 @@ void MutationObservers::NotifyAttributeChanged(Element* aElement,
int32_t aModType,
const nsAttrValue* aOldValue) {
aElement->OwnerDoc()->Changed();
Notify(aElement, NOTIFIER(AttributeChanged, aElement, aNameSpaceID,
aAttribute, aModType, aOldValue));
Notify(aElement,
NOTIFIER(AttributeChanged, aElement, aNameSpaceID, aAttribute,
aModType, aOldValue),
nsIMutationObserver::kAttributeChanged);
}
void MutationObservers::NotifyAttributeSetToCurrentValue(Element* aElement,
int32_t aNameSpaceID,
nsAtom* aAttribute) {
Notify(aElement, NOTIFIER(AttributeSetToCurrentValue, aElement, aNameSpaceID,
aAttribute));
Notify(
aElement,
NOTIFIER(AttributeSetToCurrentValue, aElement, aNameSpaceID, aAttribute),
nsIMutationObserver::kAttributeSetToCurrentValue);
}
void MutationObservers::NotifyContentAppended(nsIContent* aContainer,
nsIContent* aFirstNewContent) {
aContainer->OwnerDoc()->Changed();
Notify(aContainer, NOTIFIER(ContentAppended, aFirstNewContent));
Notify(aContainer, NOTIFIER(ContentAppended, aFirstNewContent),
nsIMutationObserver::kContentAppended);
}
void MutationObservers::NotifyContentInserted(nsINode* aContainer,
@ -159,7 +172,8 @@ void MutationObservers::NotifyContentInserted(nsINode* aContainer,
MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
"container must be an nsIContent or an Document");
aContainer->OwnerDoc()->Changed();
Notify(aContainer, NOTIFIER(ContentInserted, aChild));
Notify(aContainer, NOTIFIER(ContentInserted, aChild),
nsIMutationObserver::kContentInserted);
}
void MutationObservers::NotifyContentRemoved(nsINode* aContainer,
@ -171,21 +185,24 @@ void MutationObservers::NotifyContentRemoved(nsINode* aContainer,
MOZ_ASSERT(aChild->GetParentNode() == aContainer,
"We expect the parent link to be still around at this point");
Notify<NotifyPresShell::Before>(
aContainer, NOTIFIER(ContentRemoved, aChild, aPreviousSibling));
aContainer, NOTIFIER(ContentRemoved, aChild, aPreviousSibling),
nsIMutationObserver::kContentRemoved);
}
void MutationObservers::NotifyARIAAttributeDefaultWillChange(
mozilla::dom::Element* aElement, nsAtom* aAttribute, int32_t aModType) {
Notify<NotifyPresShell::No>(
aElement,
NOTIFIER(ARIAAttributeDefaultWillChange, aElement, aAttribute, aModType));
NOTIFIER(ARIAAttributeDefaultWillChange, aElement, aAttribute, aModType),
nsIMutationObserver::kARIAAttributeDefaultWillChange);
}
void MutationObservers::NotifyARIAAttributeDefaultChanged(
mozilla::dom::Element* aElement, nsAtom* aAttribute, int32_t aModType) {
Notify<NotifyPresShell::No>(
aElement,
NOTIFIER(ARIAAttributeDefaultChanged, aElement, aAttribute, aModType));
NOTIFIER(ARIAAttributeDefaultChanged, aElement, aAttribute, aModType),
nsIMutationObserver::kARIAAttributeDefaultChanged);
}
} // namespace mozilla

Просмотреть файл

@ -111,9 +111,11 @@ class MutationObservers {
static inline void NotifyParentChainChanged(nsIContent* aContent) {
mozilla::SafeDoublyLinkedList<nsIMutationObserver>* observers =
aContent->GetMutationObservers();
if (observers && !observers->isEmpty()) {
if (observers) {
for (auto iter = observers->begin(); iter != observers->end(); ++iter) {
iter->ParentChainChanged(aContent);
if (iter->IsCallbackEnabled(nsIMutationObserver::kParentChainChanged)) {
iter->ParentChainChanged(aContent);
}
}
}
}

Просмотреть файл

@ -398,6 +398,7 @@ nsContentList::nsContentList(nsINode* aRootNode, int32_t aMatchNameSpaceId,
}
// This is aLiveList instead of mIsLiveList to avoid Valgrind errors.
if (aLiveList) {
SetEnabledCallbacks(nsIMutationObserver::kNodeWillBeDestroyed);
mRootNode->AddMutationObserver(this);
}
@ -434,6 +435,7 @@ nsContentList::nsContentList(nsINode* aRootNode, nsContentListMatchFunc aFunc,
NS_ASSERTION(mRootNode, "Must have root");
// This is aLiveList instead of mIsLiveList to avoid Valgrind errors.
if (aLiveList) {
SetEnabledCallbacks(nsIMutationObserver::kNodeWillBeDestroyed);
mRootNode->AddMutationObserver(this);
}
@ -723,6 +725,7 @@ void nsContentList::ContentAppended(nsIContent* aFirstNewContent) {
!MayContainRelevantNodes(container) ||
(!aFirstNewContent->HasChildren() &&
!aFirstNewContent->GetNextSibling() && !MatchSelf(aFirstNewContent))) {
MaybeMarkDirty();
return;
}
@ -933,6 +936,8 @@ void nsContentList::PopulateSelf(uint32_t aNeededLength,
mState = State::Lazy;
}
SetEnabledCallbacks(nsIMutationObserver::kAll);
ASSERT_IN_SYNC;
}
@ -975,6 +980,8 @@ void nsContentList::BringSelfUpToDate(bool aDoFlush) {
PopulateSelf(uint32_t(-1));
}
mMissedUpdates = 0;
ASSERT_IN_SYNC;
NS_ASSERTION(!mRootNode || mState == State::UpToDate,
"PopulateSelf dod not bring content list up to date!");

Просмотреть файл

@ -346,6 +346,7 @@ class nsContentList : public nsBaseContentList,
mState = State::Dirty;
InvalidateNamedItemsCache();
Reset();
SetEnabledCallbacks(nsIMutationObserver::kNodeWillBeDestroyed);
}
void LastRelease() override;
@ -430,6 +431,13 @@ class nsContentList : public nsBaseContentList,
*/
void RemoveFromCaches() override { RemoveFromHashtable(); }
void MaybeMarkDirty() {
if (mState != State::Dirty && ++mMissedUpdates > 128) {
mMissedUpdates = 0;
SetDirty();
}
}
nsINode* mRootNode; // Weak ref
int32_t mMatchNameSpaceId;
RefPtr<nsAtom> mHTMLMatchAtom;
@ -451,6 +459,8 @@ class nsContentList : public nsBaseContentList,
mozilla::UniquePtr<NamedItemsCache> mNamedItemsCache;
uint8_t mMissedUpdates = 0;
// The current state of the list.
State mState;

Просмотреть файл

@ -302,6 +302,45 @@ class nsIMutationObserver
virtual void ARIAAttributeDefaultChanged(mozilla::dom::Element* aElement,
nsAtom* aAttribute,
int32_t aModType) = 0;
enum : uint32_t {
kNone = 0,
kCharacterDataWillChange = 1 << 0,
kCharacterDataChanged = 1 << 1,
kAttributeWillChange = 1 << 2,
kAttributeChanged = 1 << 3,
kAttributeSetToCurrentValue = 1 << 4,
kContentAppended = 1 << 5,
kContentInserted = 1 << 6,
kContentRemoved = 1 << 7,
kNodeWillBeDestroyed = 1 << 8,
kParentChainChanged = 1 << 9,
kARIAAttributeDefaultWillChange = 1 << 10,
kARIAAttributeDefaultChanged = 1 << 11,
kBeginUpdate = 1 << 12,
kEndUpdate = 1 << 13,
kBeginLoad = 1 << 14,
kEndLoad = 1 << 15,
kElementStateChanged = 1 << 16,
kAnimationAdded = 1 << 17,
kAnimationChanged = 1 << 18,
kAnimationRemoved = 1 << 19,
kAll = 0xFFFFFFFF
};
void SetEnabledCallbacks(uint32_t aCallbacks) {
mEnabledCallbacks = aCallbacks;
}
bool IsCallbackEnabled(uint32_t aCallback) const {
return mEnabledCallbacks & aCallback;
}
private:
uint32_t mEnabledCallbacks = kAll;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)

Просмотреть файл

@ -170,7 +170,7 @@ nsRange::nsRange(nsINode* aNode)
mNextEndRef(nullptr) {
// printf("Size of nsRange: %zu\n", sizeof(nsRange));
static_assert(sizeof(nsRange) <= 216,
static_assert(sizeof(nsRange) <= 224,
"nsRange size shouldn't be increased as far as possible");
}

Просмотреть файл

@ -395,7 +395,10 @@ class IMEContentObserver final : public nsStubMutationObserver,
class DocumentObserver final : public nsStubDocumentObserver {
public:
explicit DocumentObserver(IMEContentObserver& aIMEContentObserver)
: mIMEContentObserver(&aIMEContentObserver), mDocumentUpdating(0) {}
: mIMEContentObserver(&aIMEContentObserver), mDocumentUpdating(0) {
SetEnabledCallbacks(nsIMutationObserver::kBeginUpdate |
nsIMutationObserver::kEndUpdate);
}
NS_DECL_CYCLE_COLLECTION_CLASS(DocumentObserver)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS