mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1188640 - Add ChromeOnly MutationObserver.mergeAttributeRecords to speed up devtools, r=bz,bgrins
This commit is contained in:
parent
09e16890c4
commit
c4161c2908
@ -688,7 +688,11 @@ nsDOMMutationObserver::TakeRecords(
|
||||
for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
|
||||
nsRefPtr<nsDOMMutationRecord> next;
|
||||
current->mNext.swap(next);
|
||||
*aRetVal.AppendElement() = current.forget();
|
||||
if (!mMergeAttributeRecords ||
|
||||
!MergeableAttributeRecord(aRetVal.SafeLastElement(nullptr),
|
||||
current)) {
|
||||
*aRetVal.AppendElement() = current.forget();
|
||||
}
|
||||
current.swap(next);
|
||||
}
|
||||
ClearPendingRecords();
|
||||
@ -745,6 +749,21 @@ nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsDOMMutationObserver::MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
|
||||
nsDOMMutationRecord* aRecord)
|
||||
{
|
||||
MOZ_ASSERT(mMergeAttributeRecords);
|
||||
return
|
||||
aOldRecord &&
|
||||
aOldRecord->mType == nsGkAtoms::attributes &&
|
||||
aOldRecord->mType == aRecord->mType &&
|
||||
aOldRecord->mTarget == aRecord->mTarget &&
|
||||
aOldRecord->mAttrName == aRecord->mAttrName &&
|
||||
aOldRecord->mAttrNamespace.Equals(aRecord->mAttrNamespace);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::HandleMutation()
|
||||
{
|
||||
@ -776,7 +795,12 @@ nsDOMMutationObserver::HandleMutation()
|
||||
for (uint32_t i = 0; i < mPendingMutationCount; ++i) {
|
||||
nsRefPtr<nsDOMMutationRecord> next;
|
||||
current->mNext.swap(next);
|
||||
*mutations.AppendElement(mozilla::fallible) = current;
|
||||
if (!mMergeAttributeRecords ||
|
||||
!MergeableAttributeRecord(mutations.Length() ?
|
||||
mutations.LastElement().get() : nullptr,
|
||||
current)) {
|
||||
*mutations.AppendElement(mozilla::fallible) = current;
|
||||
}
|
||||
current.swap(next);
|
||||
}
|
||||
}
|
||||
|
@ -456,7 +456,8 @@ public:
|
||||
mozilla::dom::MutationCallback& aCb,
|
||||
bool aChrome)
|
||||
: mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
|
||||
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome), mId(++sCount)
|
||||
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome),
|
||||
mMergeAttributeRecords(false), mId(++sCount)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -498,6 +499,22 @@ public:
|
||||
|
||||
mozilla::dom::MutationCallback* MutationCallback() { return mCallback; }
|
||||
|
||||
bool MergeAttributeRecords()
|
||||
{
|
||||
return mMergeAttributeRecords;
|
||||
}
|
||||
|
||||
void SetMergeAttributeRecords(bool aVal)
|
||||
{
|
||||
mMergeAttributeRecords = aVal;
|
||||
}
|
||||
|
||||
// If both records are for 'attributes' type and for the same target and
|
||||
// attribute name and namespace are the same, we can skip the newer record.
|
||||
// aOldRecord->mPrevValue holds the original value, if observed.
|
||||
bool MergeableAttributeRecord(nsDOMMutationRecord* aOldRecord,
|
||||
nsDOMMutationRecord* aRecord);
|
||||
|
||||
void AppendMutationRecord(already_AddRefed<nsDOMMutationRecord> aRecord)
|
||||
{
|
||||
nsRefPtr<nsDOMMutationRecord> record = aRecord;
|
||||
@ -585,6 +602,7 @@ protected:
|
||||
|
||||
bool mWaitingForRun;
|
||||
bool mIsChrome;
|
||||
bool mMergeAttributeRecords;
|
||||
|
||||
uint64_t mId;
|
||||
|
||||
|
@ -743,7 +743,7 @@ function testStyleRemoveProperty2() {
|
||||
observer.disconnect();
|
||||
m = null;
|
||||
div.removeAttribute("data-test");
|
||||
then();
|
||||
then(testAttributeRecordMerging1);
|
||||
});
|
||||
m.observe(div, { attributes: true });
|
||||
is(div.getAttribute("style"), null, "style attribute before unsuccessful removeProperty");
|
||||
@ -751,6 +751,167 @@ function testStyleRemoveProperty2() {
|
||||
div.setAttribute("data-test", "a");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging1() {
|
||||
ok(true, "testAttributeRecordMerging1");
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 2);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, null);
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, null);
|
||||
observer.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then(testAttributeRecordMerging2);
|
||||
});
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging2() {
|
||||
ok(true, "testAttributeRecordMerging2");
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 2);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, "initial");
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, "initial");
|
||||
observer.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then(testAttributeRecordMerging3);
|
||||
});
|
||||
|
||||
div.setAttribute("foo", "initial");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "initial");
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging3() {
|
||||
ok(true, "testAttributeRecordMerging3");
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 4);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, "initial");
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, "initial");
|
||||
|
||||
is(records[2].type, "attributes");
|
||||
is(records[2].target, div);
|
||||
is(records[2].attributeName, "foo");
|
||||
is(records[2].attributeNamespace, null);
|
||||
is(records[2].oldValue, "bar_1");
|
||||
|
||||
is(records[3].type, "attributes");
|
||||
is(records[3].target, div.firstChild);
|
||||
is(records[3].attributeName, "foo");
|
||||
is(records[3].attributeNamespace, null);
|
||||
is(records[3].oldValue, "bar_1");
|
||||
|
||||
observer.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then(testAttributeRecordMerging4);
|
||||
});
|
||||
|
||||
div.setAttribute("foo", "initial");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "initial");
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
// No merging should happen.
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
}
|
||||
|
||||
function testAttributeRecordMerging4() {
|
||||
ok(true, "testAttributeRecordMerging4");
|
||||
var m = new M(function(records, observer) {
|
||||
});
|
||||
|
||||
div.setAttribute("foo", "initial");
|
||||
div.innerHTML = "<div></div>";
|
||||
div.firstChild.setAttribute("foo", "initial");
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeOldValue: true
|
||||
});
|
||||
SpecialPowers.wrap(m).mergeAttributeRecords = true;
|
||||
|
||||
div.setAttribute("foo", "bar_1");
|
||||
div.setAttribute("foo", "bar_2");
|
||||
div.firstChild.setAttribute("foo", "bar_1");
|
||||
div.firstChild.setAttribute("foo", "bar_2");
|
||||
|
||||
var records = m.takeRecords();
|
||||
|
||||
is(records.length, 2);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].target, div);
|
||||
is(records[0].attributeName, "foo");
|
||||
is(records[0].attributeNamespace, null);
|
||||
is(records[0].oldValue, "initial");
|
||||
|
||||
is(records[1].type, "attributes");
|
||||
is(records[1].target, div.firstChild);
|
||||
is(records[1].attributeName, "foo");
|
||||
is(records[1].attributeNamespace, null);
|
||||
is(records[1].oldValue, "initial");
|
||||
m.disconnect();
|
||||
div.innerHTML = "";
|
||||
div.removeAttribute("foo");
|
||||
then();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
|
@ -47,6 +47,8 @@ interface MutationObserver {
|
||||
sequence<MutationObservingInfo?> getObservingInfo();
|
||||
[ChromeOnly]
|
||||
readonly attribute MutationCallback mutationCallback;
|
||||
[ChromeOnly]
|
||||
attribute boolean mergeAttributeRecords;
|
||||
};
|
||||
|
||||
callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);
|
||||
|
@ -1471,6 +1471,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
// Create the observer on the node's actor. The node will make sure
|
||||
// the observer is cleaned up when the actor is released.
|
||||
actor.observer = new actor.rawNode.defaultView.MutationObserver(this.onMutations);
|
||||
actor.observer.mergeAttributeRecords = true;
|
||||
actor.observer.observe(node, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
@ -2736,29 +2737,7 @@ var WalkerActor = protocol.ActorClass({
|
||||
this._orphaned = new Set();
|
||||
}
|
||||
|
||||
|
||||
// Clear out any duplicate attribute mutations before sending them over
|
||||
// the protocol. Keep only the most recent change for each attribute.
|
||||
let targetMap = {};
|
||||
let filtered = pending.reverse().filter(mutation => {
|
||||
if (mutation.type === "attributes") {
|
||||
if (!targetMap[mutation.target]) {
|
||||
targetMap[mutation.target] = {};
|
||||
}
|
||||
let attributesForTarget = targetMap[mutation.target];
|
||||
|
||||
if (attributesForTarget[mutation.attributeName]) {
|
||||
// Since the array was reversed, if we've seen this attribute already
|
||||
// then this one is a duplicate and can be skipped.
|
||||
return false;
|
||||
}
|
||||
|
||||
attributesForTarget[mutation.attributeName] = true;
|
||||
}
|
||||
return true;
|
||||
}).reverse();
|
||||
|
||||
return filtered;
|
||||
return pending;
|
||||
}, {
|
||||
request: {
|
||||
cleanup: Option(0)
|
||||
|
Loading…
Reference in New Issue
Block a user