Bug 1827856 - Allow chrome code to properly observe UA widgets / NAC. r=smaug,devtools-reviewers,nchevobbe

Differential Revision: https://phabricator.services.mozilla.com/D175356
This commit is contained in:
Emilio Cobos Álvarez 2023-04-18 09:34:25 +00:00
parent 1d0d56d427
commit b8c9b55367
6 changed files with 108 additions and 30 deletions

View file

@ -271,6 +271,7 @@ class NodeActor extends Actor {
characterDataOldValue: true, characterDataOldValue: true,
childList: true, childList: true,
subtree: true, subtree: true,
chromeOnlyNodes: true,
}); });
this.mutationObserver = observer; this.mutationObserver = observer;
} }

View file

@ -69,8 +69,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMMutationRecord, mTarget,
// Observer // Observer
bool nsMutationReceiverBase::IsObservable(nsIContent* aContent) { bool nsMutationReceiverBase::IsObservable(nsIContent* aContent) {
return !aContent->ChromeOnlyAccess() && return !aContent->ChromeOnlyAccess() || ChromeOnlyNodes();
(Observer()->IsChrome() || !aContent->IsInNativeAnonymousSubtree());
} }
bool nsMutationReceiverBase::ObservesAttr(nsINode* aRegisterTarget, bool nsMutationReceiverBase::ObservesAttr(nsINode* aRegisterTarget,
@ -629,7 +628,7 @@ void nsDOMMutationObserver::RescheduleForRun() {
void nsDOMMutationObserver::Observe(nsINode& aTarget, void nsDOMMutationObserver::Observe(nsINode& aTarget,
const MutationObserverInit& aOptions, const MutationObserverInit& aOptions,
nsIPrincipal& aSubjectPrincipal, nsIPrincipal& aSubjectPrincipal,
mozilla::ErrorResult& aRv) { ErrorResult& aRv) {
bool childList = aOptions.mChildList; bool childList = aOptions.mChildList;
bool attributes = bool attributes =
aOptions.mAttributes.WasPassed() && aOptions.mAttributes.Value(); aOptions.mAttributes.WasPassed() && aOptions.mAttributes.Value();
@ -642,6 +641,7 @@ void nsDOMMutationObserver::Observe(nsINode& aTarget,
bool characterDataOldValue = aOptions.mCharacterDataOldValue.WasPassed() && bool characterDataOldValue = aOptions.mCharacterDataOldValue.WasPassed() &&
aOptions.mCharacterDataOldValue.Value(); aOptions.mCharacterDataOldValue.Value();
bool animations = aOptions.mAnimations; bool animations = aOptions.mAnimations;
bool chromeOnlyNodes = aOptions.mChromeOnlyNodes;
if (!aOptions.mAttributes.WasPassed() && if (!aOptions.mAttributes.WasPassed() &&
(aOptions.mAttributeOldValue.WasPassed() || (aOptions.mAttributeOldValue.WasPassed() ||
@ -707,6 +707,7 @@ void nsDOMMutationObserver::Observe(nsINode& aTarget,
r->SetAttributeFilter(std::move(filters)); r->SetAttributeFilter(std::move(filters));
r->SetAllAttributes(allAttrs); r->SetAllAttributes(allAttrs);
r->SetAnimations(animations); r->SetAnimations(animations);
r->SetChromeOnlyNodes(chromeOnlyNodes);
r->RemoveClones(); r->RemoveClones();
if (!aSubjectPrincipal.IsSystemPrincipal() && if (!aSubjectPrincipal.IsSystemPrincipal() &&
@ -793,10 +794,7 @@ already_AddRefed<nsDOMMutationObserver> nsDOMMutationObserver::Constructor(
aRv.Throw(NS_ERROR_FAILURE); aRv.Throw(NS_ERROR_FAILURE);
return nullptr; return nullptr;
} }
bool isChrome = nsContentUtils::IsChromeDoc(window->GetExtantDoc()); return MakeAndAddRef<nsDOMMutationObserver>(std::move(window), aCb);
RefPtr<nsDOMMutationObserver> observer =
new nsDOMMutationObserver(std::move(window), aCb, isChrome);
return observer.forget();
} }
bool nsDOMMutationObserver::MergeableAttributeRecord( bool nsDOMMutationObserver::MergeableAttributeRecord(

View file

@ -145,7 +145,7 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
mCharacterData = aCharacterData; mCharacterData = aCharacterData;
} }
bool CharacterDataOldValue() { bool CharacterDataOldValue() const {
return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue; return mParent ? mParent->CharacterDataOldValue() : mCharacterDataOldValue;
} }
void SetCharacterDataOldValue(bool aOldValue) { void SetCharacterDataOldValue(bool aOldValue) {
@ -153,7 +153,7 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
mCharacterDataOldValue = aOldValue; mCharacterDataOldValue = aOldValue;
} }
bool NativeAnonymousChildList() { bool NativeAnonymousChildList() const {
return mParent ? mParent->NativeAnonymousChildList() return mParent ? mParent->NativeAnonymousChildList()
: mNativeAnonymousChildList; : mNativeAnonymousChildList;
} }
@ -162,13 +162,15 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
mNativeAnonymousChildList = aOldValue; mNativeAnonymousChildList = aOldValue;
} }
bool Attributes() { return mParent ? mParent->Attributes() : mAttributes; } bool Attributes() const {
return mParent ? mParent->Attributes() : mAttributes;
}
void SetAttributes(bool aAttributes) { void SetAttributes(bool aAttributes) {
NS_ASSERTION(!mParent, "Shouldn't have parent"); NS_ASSERTION(!mParent, "Shouldn't have parent");
mAttributes = aAttributes; mAttributes = aAttributes;
} }
bool AllAttributes() { bool AllAttributes() const {
return mParent ? mParent->AllAttributes() : mAllAttributes; return mParent ? mParent->AllAttributes() : mAllAttributes;
} }
void SetAllAttributes(bool aAll) { void SetAllAttributes(bool aAll) {
@ -176,13 +178,15 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
mAllAttributes = aAll; mAllAttributes = aAll;
} }
bool Animations() { return mParent ? mParent->Animations() : mAnimations; } bool Animations() const {
return mParent ? mParent->Animations() : mAnimations;
}
void SetAnimations(bool aAnimations) { void SetAnimations(bool aAnimations) {
NS_ASSERTION(!mParent, "Shouldn't have parent"); NS_ASSERTION(!mParent, "Shouldn't have parent");
mAnimations = aAnimations; mAnimations = aAnimations;
} }
bool AttributeOldValue() { bool AttributeOldValue() const {
return mParent ? mParent->AttributeOldValue() : mAttributeOldValue; return mParent ? mParent->AttributeOldValue() : mAttributeOldValue;
} }
void SetAttributeOldValue(bool aOldValue) { void SetAttributeOldValue(bool aOldValue) {
@ -190,6 +194,15 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
mAttributeOldValue = aOldValue; mAttributeOldValue = aOldValue;
} }
bool ChromeOnlyNodes() const {
return mParent ? mParent->ChromeOnlyNodes() : mChromeOnlyNodes;
}
void SetChromeOnlyNodes(bool aChromeOnlyNodes) {
NS_ASSERTION(!mParent, "Shouldn't have parent");
mChromeOnlyNodes = aChromeOnlyNodes;
}
nsTArray<RefPtr<nsAtom>>& AttributeFilter() { return mAttributeFilter; } nsTArray<RefPtr<nsAtom>>& AttributeFilter() { return mAttributeFilter; }
void SetAttributeFilter(nsTArray<RefPtr<nsAtom>>&& aFilter) { void SetAttributeFilter(nsTArray<RefPtr<nsAtom>>&& aFilter) {
NS_ASSERTION(!mParent, "Shouldn't have parent"); NS_ASSERTION(!mParent, "Shouldn't have parent");
@ -235,7 +248,8 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
mAttributes(false), mAttributes(false),
mAllAttributes(false), mAllAttributes(false),
mAttributeOldValue(false), mAttributeOldValue(false),
mAnimations(false) { mAnimations(false),
mChromeOnlyNodes(false) {
NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!"); NS_ASSERTION(mParent->Subtree(), "Should clone a non-subtree observer!");
} }
@ -265,16 +279,17 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
nsCOMPtr<nsINode> mKungFuDeathGrip; nsCOMPtr<nsINode> mKungFuDeathGrip;
private: private:
bool mSubtree;
bool mChildList;
bool mCharacterData;
bool mCharacterDataOldValue;
bool mNativeAnonymousChildList;
bool mAttributes;
bool mAllAttributes;
bool mAttributeOldValue;
bool mAnimations;
nsTArray<RefPtr<nsAtom>> mAttributeFilter; nsTArray<RefPtr<nsAtom>> mAttributeFilter;
bool mSubtree : 1;
bool mChildList : 1;
bool mCharacterData : 1;
bool mCharacterDataOldValue : 1;
bool mNativeAnonymousChildList : 1;
bool mAttributes : 1;
bool mAllAttributes : 1;
bool mAttributeOldValue : 1;
bool mAnimations : 1;
bool mChromeOnlyNodes : 1;
}; };
class nsMutationReceiver : public nsMutationReceiverBase { class nsMutationReceiver : public nsMutationReceiverBase {
@ -412,13 +427,12 @@ class nsAnimationReceiver : public nsMutationReceiver {
class nsDOMMutationObserver final : public nsISupports, public nsWrapperCache { class nsDOMMutationObserver final : public nsISupports, public nsWrapperCache {
public: public:
nsDOMMutationObserver(nsCOMPtr<nsPIDOMWindowInner>&& aOwner, nsDOMMutationObserver(nsCOMPtr<nsPIDOMWindowInner>&& aOwner,
mozilla::dom::MutationCallback& aCb, bool aChrome) mozilla::dom::MutationCallback& aCb)
: mOwner(std::move(aOwner)), : mOwner(std::move(aOwner)),
mLastPendingMutation(nullptr), mLastPendingMutation(nullptr),
mPendingMutationCount(0), mPendingMutationCount(0),
mCallback(&aCb), mCallback(&aCb),
mWaitingForRun(false), mWaitingForRun(false),
mIsChrome(aChrome),
mMergeAttributeRecords(false), mMergeAttributeRecords(false),
mId(++sCount) {} mId(++sCount) {}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -426,8 +440,8 @@ class nsDOMMutationObserver final : public nsISupports, public nsWrapperCache {
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID) NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_MUTATION_OBSERVER_IID)
static already_AddRefed<nsDOMMutationObserver> Constructor( static already_AddRefed<nsDOMMutationObserver> Constructor(
const mozilla::dom::GlobalObject& aGlobal, const mozilla::dom::GlobalObject&, mozilla::dom::MutationCallback&,
mozilla::dom::MutationCallback& aCb, mozilla::ErrorResult& aRv); mozilla::ErrorResult&);
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override { JS::Handle<JSObject*> aGivenProto) override {
@ -436,8 +450,6 @@ class nsDOMMutationObserver final : public nsISupports, public nsWrapperCache {
nsISupports* GetParentObject() const { return mOwner; } nsISupports* GetParentObject() const { return mOwner; }
bool IsChrome() const { return mIsChrome; }
void Observe(nsINode& aTarget, void Observe(nsINode& aTarget,
const mozilla::dom::MutationObserverInit& aOptions, const mozilla::dom::MutationObserverInit& aOptions,
nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aRv); nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aRv);
@ -561,7 +573,6 @@ class nsDOMMutationObserver final : public nsISupports, public nsWrapperCache {
RefPtr<mozilla::dom::MutationCallback> mCallback; RefPtr<mozilla::dom::MutationCallback> mCallback;
bool mWaitingForRun; bool mWaitingForRun;
const bool mIsChrome;
bool mMergeAttributeRecords; bool mMergeAttributeRecords;
uint64_t mId; uint64_t mId;

View file

@ -68,6 +68,8 @@ dictionary MutationObserverInit {
[ChromeOnly] [ChromeOnly]
boolean nativeAnonymousChildList = false; boolean nativeAnonymousChildList = false;
[ChromeOnly] [ChromeOnly]
boolean chromeOnlyNodes = false;
[ChromeOnly]
boolean animations = false; boolean animations = false;
sequence<DOMString> attributeFilter; sequence<DOMString> attributeFilter;
}; };

View file

@ -27,6 +27,7 @@ support-files =
[test_audiocontrols_dimensions.html] [test_audiocontrols_dimensions.html]
[test_audiocontrols_fullscreen.html] [test_audiocontrols_fullscreen.html]
[test_mousecapture_area.html] [test_mousecapture_area.html]
[test_nac_mutations.html]
[test_panel_list_shadow_node_anchor.html] [test_panel_list_shadow_node_anchor.html]
support-files = support-files =
../../widgets/panel-item.css ../../widgets/panel-item.css

View file

@ -0,0 +1,65 @@
<!DOCTYPE HTML>
<title>UA Widget mutation observer test</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
<video controls id="video"></video>
<div style="overflow: scroll; width: 100px; height: 100px" id="scroller"></div>
<script>
const video = document.getElementById("video");
const scroller = document.getElementById("scroller");
async function test_mutations_internal(observedNode, elementToMutate, expectMutations) {
let resolveMutations;
let mutations = new Promise(r => {
resolveMutations = r;
});
let observer = new MutationObserver(function(m) {
ok(expectMutations, "Mutations should be expected");
resolveMutations(m)
});
SpecialPowers.wrap(observer).observe(observedNode, {
subtree: true,
attributes: true,
chromeOnlyNodes: expectMutations,
});
elementToMutate.setAttribute("unlikely", `value-${expectMutations}`);
if (expectMutations) {
await mutations;
} else {
await new Promise(r => SimpleTest.executeSoon(r));
}
observer.disconnect();
}
async function test_mutations(observedNode, elementToMutate) {
for (let chromeOnlyNodes of [true, false]) {
info(`Testing chromeOnlyNodes: ${chromeOnlyNodes}`);
await test_mutations_internal(observedNode, elementToMutate, chromeOnlyNodes);
}
}
add_task(async function test_ua_mutations() {
let shadow = SpecialPowers.wrap(video).openOrClosedShadowRoot;
ok(!!shadow, "UA Widget ShadowRoot exists");
await test_mutations(shadow, shadow.querySelector("*"));
});
add_task(async function test_scrollbar_mutations_same_anon_tree() {
let scrollbar = SpecialPowers.wrap(window).InspectorUtils.getChildrenForNode(scroller, true, false)[0];
is(scrollbar.tagName, "scrollbar", "should find a scrollbar");
await test_mutations(scrollbar, scrollbar);
});
add_task(async function test_scrollbar_mutations_same_tree() {
let scrollbar = SpecialPowers.wrap(window).InspectorUtils.getChildrenForNode(scroller, true, false)[0];
is(scrollbar.tagName, "scrollbar", "should find a scrollbar");
await test_mutations(scroller, scrollbar);
});
</script>