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,
childList: true,
subtree: true,
chromeOnlyNodes: true,
});
this.mutationObserver = observer;
}

View file

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

View file

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

View file

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

View file

@ -27,6 +27,7 @@ support-files =
[test_audiocontrols_dimensions.html]
[test_audiocontrols_fullscreen.html]
[test_mousecapture_area.html]
[test_nac_mutations.html]
[test_panel_list_shadow_node_anchor.html]
support-files =
../../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>