mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 1013743, MutationObserver should observe only the subtree it is attached to, r=wchen
--HG-- extra : rebase_source : c66bb5a6c5bc81163c084ddee732becd471db40e
This commit is contained in:
parent
b90b61a958
commit
e459a22bdc
@ -64,6 +64,13 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMMutationRecord,
|
||||
|
||||
// Observer
|
||||
|
||||
bool
|
||||
nsMutationReceiverBase::IsObservable(nsIContent* aContent)
|
||||
{
|
||||
return !aContent->ChromeOnlyAccess() &&
|
||||
(Observer()->IsChrome() || !aContent->IsInAnonymousSubtree());
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(nsMutationReceiver)
|
||||
NS_IMPL_RELEASE(nsMutationReceiver)
|
||||
|
||||
@ -111,8 +118,7 @@ nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
|
||||
int32_t aModType)
|
||||
{
|
||||
if (nsAutoMutationBatch::IsBatching() ||
|
||||
!ObservesAttr(aElement, aNameSpaceID, aAttribute) ||
|
||||
aElement->ChromeOnlyAccess()) {
|
||||
!ObservesAttr(RegisterTarget(), aElement, aNameSpaceID, aAttribute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -147,14 +153,16 @@ nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument,
|
||||
CharacterDataChangeInfo* aInfo)
|
||||
{
|
||||
if (nsAutoMutationBatch::IsBatching() ||
|
||||
!CharacterData() || !(Subtree() || aContent == Target()) ||
|
||||
aContent->ChromeOnlyAccess()) {
|
||||
!CharacterData() ||
|
||||
(!Subtree() && aContent != Target()) ||
|
||||
(Subtree() && RegisterTarget()->SubtreeRoot() != aContent->SubtreeRoot()) ||
|
||||
!IsObservable(aContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(nsGkAtoms::characterData);
|
||||
|
||||
|
||||
NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
|
||||
"Wrong target!");
|
||||
|
||||
@ -173,8 +181,11 @@ nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
|
||||
int32_t aNewIndexInContainer)
|
||||
{
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (!wantsChildList || aFirstNewContent->ChromeOnlyAccess()) {
|
||||
bool wantsChildList =
|
||||
ChildList() &&
|
||||
((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
|
||||
parent == Target());
|
||||
if (!wantsChildList || !IsObservable(aFirstNewContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -211,8 +222,11 @@ nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
|
||||
int32_t aIndexInContainer)
|
||||
{
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (!wantsChildList || aChild->ChromeOnlyAccess()) {
|
||||
bool wantsChildList =
|
||||
ChildList() &&
|
||||
((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
|
||||
parent == Target());
|
||||
if (!wantsChildList || !IsObservable(aChild)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -243,11 +257,14 @@ nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
|
||||
int32_t aIndexInContainer,
|
||||
nsIContent* aPreviousSibling)
|
||||
{
|
||||
if (aChild->ChromeOnlyAccess()) {
|
||||
if (!IsObservable(aChild)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
if (Subtree() && parent->SubtreeRoot() != RegisterTarget()->SubtreeRoot()) {
|
||||
return;
|
||||
}
|
||||
if (nsAutoMutationBatch::IsBatching()) {
|
||||
if (nsAutoMutationBatch::IsRemovalDone()) {
|
||||
// This can happen for example if HTML parser parses to
|
||||
@ -265,7 +282,7 @@ nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Subtree()) {
|
||||
// Try to avoid creating transient observer if the node
|
||||
@ -714,8 +731,9 @@ nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(window->IsInnerWindow());
|
||||
bool isChrome = nsContentUtils::IsChromeDoc(window->GetExtantDoc());
|
||||
nsRefPtr<nsDOMMutationObserver> observer =
|
||||
new nsDOMMutationObserver(window.forget(), aCb);
|
||||
new nsDOMMutationObserver(window.forget(), aCb, isChrome);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
|
@ -248,14 +248,20 @@ protected:
|
||||
mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
|
||||
}
|
||||
|
||||
bool ObservesAttr(mozilla::dom::Element* aElement,
|
||||
bool IsObservable(nsIContent* aContent);
|
||||
|
||||
bool ObservesAttr(nsINode* aRegisterTarget,
|
||||
mozilla::dom::Element* aElement,
|
||||
int32_t aNameSpaceID,
|
||||
nsIAtom* aAttr)
|
||||
{
|
||||
if (mParent) {
|
||||
return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
|
||||
return mParent->ObservesAttr(aRegisterTarget, aElement, aNameSpaceID, aAttr);
|
||||
}
|
||||
if (!Attributes() || (!Subtree() && aElement != Target())) {
|
||||
if (!Attributes() ||
|
||||
(!Subtree() && aElement != Target()) ||
|
||||
(Subtree() && aRegisterTarget->SubtreeRoot() != aElement->SubtreeRoot()) ||
|
||||
!IsObservable(aElement)) {
|
||||
return false;
|
||||
}
|
||||
if (AllAttributes()) {
|
||||
@ -447,9 +453,10 @@ class nsDOMMutationObserver final : public nsISupports,
|
||||
{
|
||||
public:
|
||||
nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
|
||||
mozilla::dom::MutationCallback& aCb)
|
||||
mozilla::dom::MutationCallback& aCb,
|
||||
bool aChrome)
|
||||
: mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
|
||||
mCallback(&aCb), mWaitingForRun(false), mId(++sCount)
|
||||
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome), mId(++sCount)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -471,6 +478,11 @@ public:
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
bool IsChrome()
|
||||
{
|
||||
return mIsChrome;
|
||||
}
|
||||
|
||||
void Observe(nsINode& aTarget,
|
||||
const mozilla::dom::MutationObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
@ -571,6 +583,7 @@ protected:
|
||||
nsRefPtr<mozilla::dom::MutationCallback> mCallback;
|
||||
|
||||
bool mWaitingForRun;
|
||||
bool mIsChrome;
|
||||
|
||||
uint64_t mId;
|
||||
|
||||
|
@ -20,6 +20,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=641821
|
||||
|
||||
/** Test for Bug 641821 **/
|
||||
|
||||
SimpleTest.requestFlakyTimeout("requestFlakyTimeout is silly. (But make sure marquee has time to initialize itself.)");
|
||||
|
||||
var div = document.createElement("div");
|
||||
|
||||
var M;
|
||||
@ -580,7 +582,7 @@ function testExpandos() {
|
||||
var m2 = new M(function(records, observer) {
|
||||
is(observer.expandoProperty, true);
|
||||
observer.disconnect();
|
||||
then(testStyleCreate);
|
||||
then(testOutsideShadowDOM);
|
||||
});
|
||||
m2.expandoProperty = true;
|
||||
m2.observe(div, { attributes: true });
|
||||
@ -596,6 +598,74 @@ function testExpandos() {
|
||||
div.setAttribute("foo", "bar2");
|
||||
}
|
||||
|
||||
function testOutsideShadowDOM() {
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 1);
|
||||
is(records[0].type, "attributes", "Should have got attributes");
|
||||
observer.disconnect();
|
||||
then(testInsideShadowDOM);
|
||||
});
|
||||
m.observe(div, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
})
|
||||
var sr = div.createShadowRoot();
|
||||
sr.innerHTML = "<div" + ">text</" + "div>";
|
||||
sr.firstChild.setAttribute("foo", "bar");
|
||||
sr.firstChild.firstChild.data = "text2";
|
||||
sr.firstChild.appendChild(document.createElement("div"));
|
||||
div.setAttribute("foo", "bar");
|
||||
}
|
||||
|
||||
function testInsideShadowDOM() {
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 4);
|
||||
is(records[0].type, "childList");
|
||||
is(records[1].type, "attributes");
|
||||
is(records[2].type, "characterData");
|
||||
is(records[3].type, "childList");
|
||||
observer.disconnect();
|
||||
then(testMarquee);
|
||||
});
|
||||
var sr = div.createShadowRoot();
|
||||
m.observe(sr, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
sr.innerHTML = "<div" + ">text</" + "div>";
|
||||
sr.firstChild.setAttribute("foo", "bar");
|
||||
sr.firstChild.firstChild.data = "text2";
|
||||
sr.firstChild.appendChild(document.createElement("div"));
|
||||
div.setAttribute("foo", "bar2");
|
||||
|
||||
}
|
||||
|
||||
function testMarquee() {
|
||||
var m = new M(function(records, observer) {
|
||||
is(records.length, 1);
|
||||
is(records[0].type, "attributes");
|
||||
is(records[0].attributeName, "ok");
|
||||
is(records[0].oldValue, null);
|
||||
observer.disconnect();
|
||||
then(testStyleCreate);
|
||||
});
|
||||
var marquee = document.createElement("marquee");
|
||||
m.observe(marquee, {
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true
|
||||
});
|
||||
document.body.appendChild(marquee);
|
||||
setTimeout(function() {marquee.setAttribute("ok", "ok")}, 500);
|
||||
}
|
||||
|
||||
function testStyleCreate() {
|
||||
m = new M(function(records, observer) {
|
||||
is(records.length, 1, "number of records");
|
||||
|
Loading…
Reference in New Issue
Block a user