зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1834103 - Optimize out some nsIMutationObserver calls, r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D178558
This commit is contained in:
Родитель
175fec1662
Коммит
80bb4b4f78
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче