зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1827856 - Remove nativeAnonymousChildList observers. r=smaug,credential-management-reviewers,devtools-reviewers,sgalich,nchevobbe
You let me know if this seems appealing to you :) Differential Revision: https://phabricator.services.mozilla.com/D175382
This commit is contained in:
Родитель
193c21ac76
Коммит
8b5b7ad998
|
@ -796,21 +796,6 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocAccessible::NativeAnonymousChildListChange(nsIContent* aContent,
|
|
||||||
bool aIsRemove) {
|
|
||||||
if (aIsRemove) {
|
|
||||||
#ifdef A11Y_LOG
|
|
||||||
if (logging::IsEnabled(logging::eTree)) {
|
|
||||||
logging::MsgBegin("TREE", "Anonymous content removed; doc: %p", this);
|
|
||||||
logging::Node("node", aContent);
|
|
||||||
logging::MsgEnd();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ContentRemoved(aContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DocAccessible::AttributeChanged(dom::Element* aElement,
|
void DocAccessible::AttributeChanged(dom::Element* aElement,
|
||||||
int32_t aNameSpaceID, nsAtom* aAttribute,
|
int32_t aNameSpaceID, nsAtom* aAttribute,
|
||||||
int32_t aModType,
|
int32_t aModType,
|
||||||
|
|
|
@ -216,10 +216,7 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
|
||||||
|
|
||||||
const emittedMutation = Object.assign(change, { target: targetFront });
|
const emittedMutation = Object.assign(change, { target: targetFront });
|
||||||
|
|
||||||
if (
|
if (change.type === "childList") {
|
||||||
change.type === "childList" ||
|
|
||||||
change.type === "nativeAnonymousChildList"
|
|
||||||
) {
|
|
||||||
// Update the ownership tree according to the mutation record.
|
// Update the ownership tree according to the mutation record.
|
||||||
const addedFronts = [];
|
const addedFronts = [];
|
||||||
const removedFronts = [];
|
const removedFronts = [];
|
||||||
|
@ -287,8 +284,7 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
|
||||||
if (
|
if (
|
||||||
change.type === "inlineTextChild" ||
|
change.type === "inlineTextChild" ||
|
||||||
change.type === "childList" ||
|
change.type === "childList" ||
|
||||||
change.type === "shadowRootAttached" ||
|
change.type === "shadowRootAttached"
|
||||||
change.type === "nativeAnonymousChildList"
|
|
||||||
) {
|
) {
|
||||||
if (change.inlineTextChild) {
|
if (change.inlineTextChild) {
|
||||||
targetFront.inlineTextChild = types
|
targetFront.inlineTextChild = types
|
||||||
|
|
|
@ -1571,7 +1571,6 @@ MarkupView.prototype = {
|
||||||
container.update();
|
container.update();
|
||||||
} else if (
|
} else if (
|
||||||
type === "childList" ||
|
type === "childList" ||
|
||||||
type === "nativeAnonymousChildList" ||
|
|
||||||
type === "slotchange" ||
|
type === "slotchange" ||
|
||||||
type === "shadowRootAttached"
|
type === "shadowRootAttached"
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -72,8 +72,7 @@ add_task(async function() {
|
||||||
info(
|
info(
|
||||||
"Move the non-slotted element with class has-before and check the pseudo appears"
|
"Move the non-slotted element with class has-before and check the pseudo appears"
|
||||||
);
|
);
|
||||||
const mutated = waitForNMutations(inspector, "childList", 2);
|
const mutated = waitForNMutations(inspector, "childList", 3);
|
||||||
const pseudoMutated = waitForMutation(inspector, "nativeAnonymousChildList");
|
|
||||||
SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
|
SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
|
||||||
const root = content.document.querySelector(".root");
|
const root = content.document.querySelector(".root");
|
||||||
const hasBeforeEl = content.document.querySelector(
|
const hasBeforeEl = content.document.querySelector(
|
||||||
|
@ -82,7 +81,6 @@ add_task(async function() {
|
||||||
root.appendChild(hasBeforeEl);
|
root.appendChild(hasBeforeEl);
|
||||||
});
|
});
|
||||||
await mutated;
|
await mutated;
|
||||||
await pseudoMutated;
|
|
||||||
|
|
||||||
// As the non-slotted has-before is moved into the tree, the before pseudo is expected
|
// As the non-slotted has-before is moved into the tree, the before pseudo is expected
|
||||||
// to appear.
|
// to appear.
|
||||||
|
|
|
@ -70,7 +70,7 @@ __WalkerActor__
|
||||||
- But only has a partial knowledge of the DOM (what is currently displayed/expanded in the MarkupView). It doesn't need to walk the whole tree when you first instantiate it.
|
- But only has a partial knowledge of the DOM (what is currently displayed/expanded in the MarkupView). It doesn't need to walk the whole tree when you first instantiate it.
|
||||||
- Reflects some of the usual DOM APIs like querySelector.
|
- Reflects some of the usual DOM APIs like querySelector.
|
||||||
- Note that methods like querySelector return arbitrarily nested NodeActors, in which case the WalkerActor also sends the list of parents to link the returned nodes to the closest known nodes, so the UI can display the tree correctly.
|
- Note that methods like querySelector return arbitrarily nested NodeActors, in which case the WalkerActor also sends the list of parents to link the returned nodes to the closest known nodes, so the UI can display the tree correctly.
|
||||||
- Emits events when there are DOM mutations. These events are sent to the front-end and used to, for example refresh the markup-view. This uses an instance of MutationObserver (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) configured with, in particular, nativeAnonymousChildList set to true, so that mutation events are also sent when pseudo elements are added/removed via css.
|
- Emits events when there are DOM mutations. These events are sent to the front-end and used to, for example refresh the markup-view. This uses an instance of MutationObserver (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) configured with, in particular, chromeOnlyNodes set to true, so that mutation events are also sent when pseudo elements are added/removed via css.
|
||||||
|
|
||||||
__NodeActor__
|
__NodeActor__
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,6 @@ class NodeActor extends Actor {
|
||||||
const observer = new doc.defaultView.MutationObserver(callback);
|
const observer = new doc.defaultView.MutationObserver(callback);
|
||||||
observer.mergeAttributeRecords = true;
|
observer.mergeAttributeRecords = true;
|
||||||
observer.observe(node, {
|
observer.observe(node, {
|
||||||
nativeAnonymousChildList: true,
|
|
||||||
attributes: true,
|
attributes: true,
|
||||||
characterData: true,
|
characterData: true,
|
||||||
characterDataOldValue: true,
|
characterDataOldValue: true,
|
||||||
|
|
|
@ -215,6 +215,7 @@ class WalkerActor extends Actor {
|
||||||
this._pendingMutations = [];
|
this._pendingMutations = [];
|
||||||
this._activePseudoClassLocks = new Set();
|
this._activePseudoClassLocks = new Set();
|
||||||
this._mutationBreakpoints = new WeakMap();
|
this._mutationBreakpoints = new WeakMap();
|
||||||
|
this._anonParents = new WeakMap();
|
||||||
this.customElementWatcher = new CustomElementWatcher(
|
this.customElementWatcher = new CustomElementWatcher(
|
||||||
targetActor.chromeEventHandler
|
targetActor.chromeEventHandler
|
||||||
);
|
);
|
||||||
|
@ -247,6 +248,8 @@ class WalkerActor extends Actor {
|
||||||
this.onMutations = this.onMutations.bind(this);
|
this.onMutations = this.onMutations.bind(this);
|
||||||
this.onSlotchange = this.onSlotchange.bind(this);
|
this.onSlotchange = this.onSlotchange.bind(this);
|
||||||
this.onShadowrootattached = this.onShadowrootattached.bind(this);
|
this.onShadowrootattached = this.onShadowrootattached.bind(this);
|
||||||
|
this.onAnonymousrootcreated = this.onAnonymousrootcreated.bind(this);
|
||||||
|
this.onAnonymousrootremoved = this.onAnonymousrootremoved.bind(this);
|
||||||
this.onFrameLoad = this.onFrameLoad.bind(this);
|
this.onFrameLoad = this.onFrameLoad.bind(this);
|
||||||
this.onFrameUnload = this.onFrameUnload.bind(this);
|
this.onFrameUnload = this.onFrameUnload.bind(this);
|
||||||
this.onCustomElementDefined = this.onCustomElementDefined.bind(this);
|
this.onCustomElementDefined = this.onCustomElementDefined.bind(this);
|
||||||
|
@ -271,9 +274,17 @@ class WalkerActor extends Actor {
|
||||||
"shadowrootattached",
|
"shadowrootattached",
|
||||||
this.onShadowrootattached
|
this.onShadowrootattached
|
||||||
);
|
);
|
||||||
|
// anonymousrootcreated is a chrome-only event. We enable it below.
|
||||||
|
this.chromeEventHandler.addEventListener(
|
||||||
|
"anonymousrootcreated",
|
||||||
|
this.onAnonymousrootcreated
|
||||||
|
);
|
||||||
|
this.chromeEventHandler.addEventListener(
|
||||||
|
"anonymousrootremoved",
|
||||||
|
this.onAnonymousrootremoved
|
||||||
|
);
|
||||||
for (const { document } of this.targetActor.windows) {
|
for (const { document } of this.targetActor.windows) {
|
||||||
document.shadowRootAttachedEventEnabled = true;
|
document.devToolsAnonymousAndShadowEventsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the root document node actor is ready and
|
// Ensure that the root document node actor is ready and
|
||||||
|
@ -384,10 +395,18 @@ class WalkerActor extends Actor {
|
||||||
"shadowrootattached",
|
"shadowrootattached",
|
||||||
this.onShadowrootattached
|
this.onShadowrootattached
|
||||||
);
|
);
|
||||||
|
this.chromeEventHandler.removeEventListener(
|
||||||
|
"anonymousrootcreated",
|
||||||
|
this.onAnonymousrootcreated
|
||||||
|
);
|
||||||
|
this.chromeEventHandler.removeEventListener(
|
||||||
|
"anonymousrootremoved",
|
||||||
|
this.onAnonymousrootremoved
|
||||||
|
);
|
||||||
|
|
||||||
// This event is just for devtools, so we can unset once we're done.
|
// This attribute is just for devtools, so we can unset once we're done.
|
||||||
for (const { document } of this.targetActor.windows) {
|
for (const { document } of this.targetActor.windows) {
|
||||||
document.shadowRootAttachedEventEnabled = false;
|
document.devToolsAnonymousAndShadowEventsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onFrameLoad = null;
|
this.onFrameLoad = null;
|
||||||
|
@ -2169,7 +2188,7 @@ class WalkerActor extends Actor {
|
||||||
} else if (type === "characterData") {
|
} else if (type === "characterData") {
|
||||||
mutation.newValue = targetNode.nodeValue;
|
mutation.newValue = targetNode.nodeValue;
|
||||||
this._maybeQueueInlineTextChildMutation(change, targetNode);
|
this._maybeQueueInlineTextChildMutation(change, targetNode);
|
||||||
} else if (type === "childList" || type === "nativeAnonymousChildList") {
|
} else if (type === "childList") {
|
||||||
// Get the list of removed and added actors that the client has seen
|
// Get the list of removed and added actors that the client has seen
|
||||||
// so that it can keep its ownership tree up to date.
|
// so that it can keep its ownership tree up to date.
|
||||||
const removedActors = [];
|
const removedActors = [];
|
||||||
|
@ -2262,6 +2281,52 @@ class WalkerActor extends Actor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires when an anonymous root is created.
|
||||||
|
* This is needed because regular mutation observers don't fire on some kinds
|
||||||
|
* of NAC creation. We want to treat this like a regular insertion.
|
||||||
|
*/
|
||||||
|
onAnonymousrootcreated(event) {
|
||||||
|
const root = event.target;
|
||||||
|
const parent = this.rawParentNode(root);
|
||||||
|
if (!parent) {
|
||||||
|
// These events are async. The node might have been removed already, in
|
||||||
|
// which case there's nothing to do anymore.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// By the time onAnonymousrootremoved fires, the node is already detached
|
||||||
|
// from its parent, so we need to remember it by hand.
|
||||||
|
this._anonParents.set(root, parent);
|
||||||
|
this.onMutations([
|
||||||
|
{
|
||||||
|
type: "childList",
|
||||||
|
target: parent,
|
||||||
|
addedNodes: [root],
|
||||||
|
removedNodes: [],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see onAnonymousrootcreated
|
||||||
|
*/
|
||||||
|
onAnonymousrootremoved(event) {
|
||||||
|
const root = event.target;
|
||||||
|
const parent = this._anonParents.get(root);
|
||||||
|
if (!parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._anonParents.delete(root);
|
||||||
|
this.onMutations([
|
||||||
|
{
|
||||||
|
type: "childList",
|
||||||
|
target: parent,
|
||||||
|
addedNodes: [],
|
||||||
|
removedNodes: [root],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
onShadowrootattached(event) {
|
onShadowrootattached(event) {
|
||||||
const actor = this.getNode(event.target);
|
const actor = this.getNode(event.target);
|
||||||
if (!actor) {
|
if (!actor) {
|
||||||
|
|
|
@ -459,9 +459,6 @@ nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MutationObservers::NotifyParentChainChanged(this);
|
MutationObservers::NotifyParentChainChanged(this);
|
||||||
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
|
|
||||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateEditableState(false);
|
UpdateEditableState(false);
|
||||||
|
|
||||||
|
@ -488,9 +485,6 @@ void CharacterData::UnbindFromTree(bool aNullParent) {
|
||||||
HandleShadowDOMRelatedRemovalSteps(aNullParent);
|
HandleShadowDOMRelatedRemovalSteps(aNullParent);
|
||||||
|
|
||||||
if (aNullParent) {
|
if (aNullParent) {
|
||||||
if (IsRootOfNativeAnonymousSubtree()) {
|
|
||||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, true);
|
|
||||||
}
|
|
||||||
if (GetParent()) {
|
if (GetParent()) {
|
||||||
NS_RELEASE(mParent);
|
NS_RELEASE(mParent);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1366,7 +1366,7 @@ Document::Document(const char* aContentType)
|
||||||
mBFCacheDisallowed(false),
|
mBFCacheDisallowed(false),
|
||||||
mHasHadDefaultView(false),
|
mHasHadDefaultView(false),
|
||||||
mStyleSheetChangeEventsEnabled(false),
|
mStyleSheetChangeEventsEnabled(false),
|
||||||
mShadowRootAttachedEventEnabled(false),
|
mDevToolsAnonymousAndShadowEventsEnabled(false),
|
||||||
mIsSrcdocDocument(false),
|
mIsSrcdocDocument(false),
|
||||||
mHasDisplayDocument(false),
|
mHasDisplayDocument(false),
|
||||||
mFontFaceSetDirty(true),
|
mFontFaceSetDirty(true),
|
||||||
|
|
|
@ -3602,11 +3602,11 @@ class Document : public nsINode,
|
||||||
return mStyleSheetChangeEventsEnabled;
|
return mStyleSheetChangeEventsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetShadowRootAttachedEventEnabled(bool aValue) {
|
void SetDevToolsAnonymousAndShadowEventsEnabled(bool aValue) {
|
||||||
mShadowRootAttachedEventEnabled = aValue;
|
mDevToolsAnonymousAndShadowEventsEnabled = aValue;
|
||||||
}
|
}
|
||||||
bool ShadowRootAttachedEventEnabled() const {
|
bool DevToolsAnonymousAndShadowEventsEnabled() const {
|
||||||
return mShadowRootAttachedEventEnabled;
|
return mDevToolsAnonymousAndShadowEventsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> BlockParsing(Promise& aPromise,
|
already_AddRefed<Promise> BlockParsing(Promise& aPromise,
|
||||||
|
@ -4740,8 +4740,9 @@ class Document : public nsINode,
|
||||||
// Whether style sheet change events will be dispatched for this document
|
// Whether style sheet change events will be dispatched for this document
|
||||||
bool mStyleSheetChangeEventsEnabled : 1;
|
bool mStyleSheetChangeEventsEnabled : 1;
|
||||||
|
|
||||||
// Whether shadowrootattached events will be dispatched for this document.
|
// Whether shadowrootattached/anonymousnodecreated/anonymousnoderemoved events
|
||||||
bool mShadowRootAttachedEventEnabled : 1;
|
// will be dispatched for this document.
|
||||||
|
bool mDevToolsAnonymousAndShadowEventsEnabled : 1;
|
||||||
|
|
||||||
// Whether the document was created by a srcdoc iframe.
|
// Whether the document was created by a srcdoc iframe.
|
||||||
bool mIsSrcdocDocument : 1;
|
bool mIsSrcdocDocument : 1;
|
||||||
|
|
|
@ -1275,10 +1275,10 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
|
||||||
SlotAssignmentMode aSlotAssignment) {
|
SlotAssignmentMode aSlotAssignment) {
|
||||||
nsAutoScriptBlocker scriptBlocker;
|
nsAutoScriptBlocker scriptBlocker;
|
||||||
|
|
||||||
|
auto* nim = mNodeInfo->NodeInfoManager();
|
||||||
RefPtr<mozilla::dom::NodeInfo> nodeInfo =
|
RefPtr<mozilla::dom::NodeInfo> nodeInfo =
|
||||||
mNodeInfo->NodeInfoManager()->GetNodeInfo(
|
nim->GetNodeInfo(nsGkAtoms::documentFragmentNodeName, nullptr,
|
||||||
nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
|
kNameSpaceID_None, DOCUMENT_FRAGMENT_NODE);
|
||||||
DOCUMENT_FRAGMENT_NODE);
|
|
||||||
|
|
||||||
// If there are no children, the flat tree is not changing due to the presence
|
// If there are no children, the flat tree is not changing due to the presence
|
||||||
// of the shadow root, so we don't need to invalidate style / layout.
|
// of the shadow root, so we don't need to invalidate style / layout.
|
||||||
|
@ -1296,7 +1296,6 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
|
||||||
* context object's node document, host is context object,
|
* context object's node document, host is context object,
|
||||||
* and mode is init's mode.
|
* and mode is init's mode.
|
||||||
*/
|
*/
|
||||||
auto* nim = nodeInfo->NodeInfoManager();
|
|
||||||
RefPtr<ShadowRoot> shadowRoot = new (nim) ShadowRoot(
|
RefPtr<ShadowRoot> shadowRoot = new (nim) ShadowRoot(
|
||||||
this, aMode, aDelegatesFocus, aSlotAssignment, nodeInfo.forget());
|
this, aMode, aDelegatesFocus, aSlotAssignment, nodeInfo.forget());
|
||||||
|
|
||||||
|
@ -1320,7 +1319,8 @@ already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
|
||||||
SetShadowRoot(shadowRoot);
|
SetShadowRoot(shadowRoot);
|
||||||
|
|
||||||
// Dispatch a "shadowrootattached" event for devtools if needed.
|
// Dispatch a "shadowrootattached" event for devtools if needed.
|
||||||
if (MOZ_UNLIKELY(nim->GetDocument()->ShadowRootAttachedEventEnabled())) {
|
if (MOZ_UNLIKELY(
|
||||||
|
nim->GetDocument()->DevToolsAnonymousAndShadowEventsEnabled())) {
|
||||||
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
|
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
|
||||||
this, u"shadowrootattached"_ns, CanBubble::eYes,
|
this, u"shadowrootattached"_ns, CanBubble::eYes,
|
||||||
ChromeOnlyDispatch::eYes, Composed::eYes);
|
ChromeOnlyDispatch::eYes, Composed::eYes);
|
||||||
|
@ -1899,9 +1899,6 @@ nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MutationObservers::NotifyParentChainChanged(this);
|
MutationObservers::NotifyParentChainChanged(this);
|
||||||
if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
|
|
||||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we only run this once, in the case we move the ShadowRoot around.
|
// Ensure we only run this once, in the case we move the ShadowRoot around.
|
||||||
if (aContext.SubtreeRootChanges()) {
|
if (aContext.SubtreeRootChanges()) {
|
||||||
|
@ -2012,10 +2009,6 @@ void Element::UnbindFromTree(bool aNullParent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aNullParent) {
|
if (aNullParent) {
|
||||||
if (IsRootOfNativeAnonymousSubtree()) {
|
|
||||||
MutationObservers::NotifyNativeAnonymousChildListChange(this, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetParent()) {
|
if (GetParent()) {
|
||||||
RefPtr<nsINode> p;
|
RefPtr<nsINode> p;
|
||||||
p.swap(mParent);
|
p.swap(mParent);
|
||||||
|
|
|
@ -163,19 +163,6 @@ void MutationObservers::NotifyContentAppended(nsIContent* aContainer,
|
||||||
Notify(aContainer, notifyPresShell, notifyObserver);
|
Notify(aContainer, notifyPresShell, notifyObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MutationObservers::NotifyNativeAnonymousChildListChange(
|
|
||||||
nsIContent* aContent, bool aIsRemove) {
|
|
||||||
DEFINE_NOTIFIERS(NativeAnonymousChildListChange, (aContent, aIsRemove));
|
|
||||||
if (aIsRemove) {
|
|
||||||
// We can't actually assert that we reach the document if we're connected,
|
|
||||||
// since this notification runs from UnbindFromTree.
|
|
||||||
Notify<IsRemoval::Yes, ShouldAssert::No>(aContent, notifyPresShell,
|
|
||||||
notifyObserver);
|
|
||||||
} else {
|
|
||||||
Notify(aContent, notifyPresShell, notifyObserver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MutationObservers::NotifyContentInserted(nsINode* aContainer,
|
void MutationObservers::NotifyContentInserted(nsINode* aContainer,
|
||||||
nsIContent* aChild) {
|
nsIContent* aChild) {
|
||||||
MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
|
MOZ_ASSERT(aContainer->IsContent() || aContainer->IsDocument(),
|
||||||
|
|
|
@ -88,15 +88,6 @@ class MutationObservers {
|
||||||
static void NotifyContentAppended(nsIContent* aContainer,
|
static void NotifyContentAppended(nsIContent* aContainer,
|
||||||
nsIContent* aFirstNewContent);
|
nsIContent* aFirstNewContent);
|
||||||
|
|
||||||
/**
|
|
||||||
* Send NativeAnonymousChildList notifications to nsIMutationObservers
|
|
||||||
* @param aContent Anonymous node that's been added or removed
|
|
||||||
* @param aIsRemove True if it's a removal, false if an addition
|
|
||||||
* @see nsIMutationObserver::NativeAnonymousChildListChange
|
|
||||||
*/
|
|
||||||
static void NotifyNativeAnonymousChildListChange(nsIContent* aContent,
|
|
||||||
bool aIsRemove);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send ContentInserted notifications to nsIMutationObservers
|
* Send ContentInserted notifications to nsIMutationObservers
|
||||||
* @param aContainer Node into which new child was inserted
|
* @param aContainer Node into which new child was inserted
|
||||||
|
|
|
@ -138,35 +138,6 @@ void nsMutationReceiver::Disconnect(bool aRemoveFromObserver) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsMutationReceiver::NativeAnonymousChildListChange(nsIContent* aContent,
|
|
||||||
bool aIsRemove) {
|
|
||||||
if (!NativeAnonymousChildList()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsINode* parent = aContent->GetParentNode();
|
|
||||||
if (!parent || (!Subtree() && Target() != parent) ||
|
|
||||||
(Subtree() && RegisterTarget()->SubtreeRoot() != parent->SubtreeRoot())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsDOMMutationRecord* m =
|
|
||||||
Observer()->CurrentRecord(nsGkAtoms::nativeAnonymousChildList);
|
|
||||||
|
|
||||||
if (m->mTarget) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m->mTarget = parent;
|
|
||||||
|
|
||||||
if (aIsRemove) {
|
|
||||||
m->mRemovedNodes = new nsSimpleContentList(parent);
|
|
||||||
m->mRemovedNodes->AppendElement(aContent);
|
|
||||||
} else {
|
|
||||||
m->mAddedNodes = new nsSimpleContentList(parent);
|
|
||||||
m->mAddedNodes->AppendElement(aContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void nsMutationReceiver::AttributeWillChange(Element* aElement,
|
void nsMutationReceiver::AttributeWillChange(Element* aElement,
|
||||||
int32_t aNameSpaceID,
|
int32_t aNameSpaceID,
|
||||||
nsAtom* aAttribute,
|
nsAtom* aAttribute,
|
||||||
|
@ -637,7 +608,6 @@ void nsDOMMutationObserver::Observe(nsINode& aTarget,
|
||||||
bool subtree = aOptions.mSubtree;
|
bool subtree = aOptions.mSubtree;
|
||||||
bool attributeOldValue = aOptions.mAttributeOldValue.WasPassed() &&
|
bool attributeOldValue = aOptions.mAttributeOldValue.WasPassed() &&
|
||||||
aOptions.mAttributeOldValue.Value();
|
aOptions.mAttributeOldValue.Value();
|
||||||
bool nativeAnonymousChildList = aOptions.mNativeAnonymousChildList;
|
|
||||||
bool characterDataOldValue = aOptions.mCharacterDataOldValue.WasPassed() &&
|
bool characterDataOldValue = aOptions.mCharacterDataOldValue.WasPassed() &&
|
||||||
aOptions.mCharacterDataOldValue.Value();
|
aOptions.mCharacterDataOldValue.Value();
|
||||||
bool animations = aOptions.mAnimations;
|
bool animations = aOptions.mAnimations;
|
||||||
|
@ -654,8 +624,7 @@ void nsDOMMutationObserver::Observe(nsINode& aTarget,
|
||||||
characterData = true;
|
characterData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(childList || attributes || characterData || animations ||
|
if (!(childList || attributes || characterData || animations)) {
|
||||||
nativeAnonymousChildList)) {
|
|
||||||
aRv.ThrowTypeError(
|
aRv.ThrowTypeError(
|
||||||
"One of 'childList', 'attributes', 'characterData' must not be false.");
|
"One of 'childList', 'attributes', 'characterData' must not be false.");
|
||||||
return;
|
return;
|
||||||
|
@ -703,7 +672,6 @@ void nsDOMMutationObserver::Observe(nsINode& aTarget,
|
||||||
r->SetSubtree(subtree);
|
r->SetSubtree(subtree);
|
||||||
r->SetAttributeOldValue(attributeOldValue);
|
r->SetAttributeOldValue(attributeOldValue);
|
||||||
r->SetCharacterDataOldValue(characterDataOldValue);
|
r->SetCharacterDataOldValue(characterDataOldValue);
|
||||||
r->SetNativeAnonymousChildList(nativeAnonymousChildList);
|
|
||||||
r->SetAttributeFilter(std::move(filters));
|
r->SetAttributeFilter(std::move(filters));
|
||||||
r->SetAllAttributes(allAttrs);
|
r->SetAllAttributes(allAttrs);
|
||||||
r->SetAnimations(animations);
|
r->SetAnimations(animations);
|
||||||
|
@ -765,7 +733,6 @@ void nsDOMMutationObserver::GetObservingInfo(
|
||||||
info.mSubtree = mr->Subtree();
|
info.mSubtree = mr->Subtree();
|
||||||
info.mAttributeOldValue.Construct(mr->AttributeOldValue());
|
info.mAttributeOldValue.Construct(mr->AttributeOldValue());
|
||||||
info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
|
info.mCharacterDataOldValue.Construct(mr->CharacterDataOldValue());
|
||||||
info.mNativeAnonymousChildList = mr->NativeAnonymousChildList();
|
|
||||||
info.mAnimations = mr->Animations();
|
info.mAnimations = mr->Animations();
|
||||||
nsTArray<RefPtr<nsAtom>>& filters = mr->AttributeFilter();
|
nsTArray<RefPtr<nsAtom>>& filters = mr->AttributeFilter();
|
||||||
if (filters.Length()) {
|
if (filters.Length()) {
|
||||||
|
|
|
@ -153,15 +153,6 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
|
||||||
mCharacterDataOldValue = aOldValue;
|
mCharacterDataOldValue = aOldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeAnonymousChildList() const {
|
|
||||||
return mParent ? mParent->NativeAnonymousChildList()
|
|
||||||
: mNativeAnonymousChildList;
|
|
||||||
}
|
|
||||||
void SetNativeAnonymousChildList(bool aOldValue) {
|
|
||||||
NS_ASSERTION(!mParent, "Shouldn't have parent");
|
|
||||||
mNativeAnonymousChildList = aOldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Attributes() const {
|
bool Attributes() const {
|
||||||
return mParent ? mParent->Attributes() : mAttributes;
|
return mParent ? mParent->Attributes() : mAttributes;
|
||||||
}
|
}
|
||||||
|
@ -227,7 +218,6 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
|
||||||
mChildList(false),
|
mChildList(false),
|
||||||
mCharacterData(false),
|
mCharacterData(false),
|
||||||
mCharacterDataOldValue(false),
|
mCharacterDataOldValue(false),
|
||||||
mNativeAnonymousChildList(false),
|
|
||||||
mAttributes(false),
|
mAttributes(false),
|
||||||
mAllAttributes(false),
|
mAllAttributes(false),
|
||||||
mAttributeOldValue(false),
|
mAttributeOldValue(false),
|
||||||
|
@ -244,7 +234,6 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
|
||||||
mChildList(false),
|
mChildList(false),
|
||||||
mCharacterData(false),
|
mCharacterData(false),
|
||||||
mCharacterDataOldValue(false),
|
mCharacterDataOldValue(false),
|
||||||
mNativeAnonymousChildList(false),
|
|
||||||
mAttributes(false),
|
mAttributes(false),
|
||||||
mAllAttributes(false),
|
mAllAttributes(false),
|
||||||
mAttributeOldValue(false),
|
mAttributeOldValue(false),
|
||||||
|
@ -284,7 +273,6 @@ class nsMutationReceiverBase : public nsStubAnimationObserver {
|
||||||
bool mChildList : 1;
|
bool mChildList : 1;
|
||||||
bool mCharacterData : 1;
|
bool mCharacterData : 1;
|
||||||
bool mCharacterDataOldValue : 1;
|
bool mCharacterDataOldValue : 1;
|
||||||
bool mNativeAnonymousChildList : 1;
|
|
||||||
bool mAttributes : 1;
|
bool mAttributes : 1;
|
||||||
bool mAllAttributes : 1;
|
bool mAllAttributes : 1;
|
||||||
bool mAttributeOldValue : 1;
|
bool mAttributeOldValue : 1;
|
||||||
|
@ -341,7 +329,6 @@ class nsMutationReceiver : public nsMutationReceiverBase {
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
|
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
|
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE
|
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
|
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||||
|
|
|
@ -204,16 +204,6 @@ class nsIMutationObserver
|
||||||
int32_t aModType,
|
int32_t aModType,
|
||||||
const nsAttrValue* aOldValue) = 0;
|
const nsAttrValue* aOldValue) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Notification that the root of a native anonymous has been added
|
|
||||||
* or removed.
|
|
||||||
*
|
|
||||||
* @param aContent Anonymous node that's been added or removed
|
|
||||||
* @param aIsRemove True if it's a removal, false if an addition
|
|
||||||
*/
|
|
||||||
virtual void NativeAnonymousChildListChange(nsIContent* aContent,
|
|
||||||
bool aIsRemove) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification that an attribute of an element has been
|
* Notification that an attribute of an element has been
|
||||||
* set to the value it already had.
|
* set to the value it already had.
|
||||||
|
@ -331,10 +321,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)
|
||||||
int32_t aNameSpaceID, nsAtom* aAttribute, \
|
int32_t aNameSpaceID, nsAtom* aAttribute, \
|
||||||
int32_t aModType) override;
|
int32_t aModType) override;
|
||||||
|
|
||||||
#define NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE \
|
|
||||||
virtual void NativeAnonymousChildListChange(nsIContent* aContent, \
|
|
||||||
bool aIsRemove) override;
|
|
||||||
|
|
||||||
#define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \
|
#define NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \
|
||||||
virtual void AttributeChanged(mozilla::dom::Element* aElement, \
|
virtual void AttributeChanged(mozilla::dom::Element* aElement, \
|
||||||
int32_t aNameSpaceID, nsAtom* aAttribute, \
|
int32_t aNameSpaceID, nsAtom* aAttribute, \
|
||||||
|
@ -371,7 +357,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE \
|
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE \
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED \
|
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED \
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE \
|
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE \
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_NATIVEANONYMOUSCHILDLISTCHANGE \
|
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \
|
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED \
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED \
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED \
|
||||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED \
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED \
|
||||||
|
@ -392,8 +377,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIMutationObserver, NS_IMUTATION_OBSERVER_IID)
|
||||||
void _class::AttributeWillChange(mozilla::dom::Element* aElement, \
|
void _class::AttributeWillChange(mozilla::dom::Element* aElement, \
|
||||||
int32_t aNameSpaceID, nsAtom* aAttribute, \
|
int32_t aNameSpaceID, nsAtom* aAttribute, \
|
||||||
int32_t aModType) {} \
|
int32_t aModType) {} \
|
||||||
void _class::NativeAnonymousChildListChange(nsIContent* aContent, \
|
|
||||||
bool aIsRemove) {} \
|
|
||||||
void _class::AttributeChanged( \
|
void _class::AttributeChanged( \
|
||||||
mozilla::dom::Element* aElement, int32_t aNameSpaceID, \
|
mozilla::dom::Element* aElement, int32_t aNameSpaceID, \
|
||||||
nsAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) {} \
|
nsAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) {} \
|
||||||
|
|
|
@ -443,6 +443,15 @@ Element* nsINode::GetAnonymousRootElementOfTextEditor(
|
||||||
return rootElement;
|
return rootElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nsINode::QueueDevtoolsAnonymousEvent(bool aIsRemove) {
|
||||||
|
MOZ_ASSERT(IsRootOfNativeAnonymousSubtree());
|
||||||
|
MOZ_ASSERT(OwnerDoc()->DevToolsAnonymousAndShadowEventsEnabled());
|
||||||
|
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
|
||||||
|
this, aIsRemove ? u"anonymousrootremoved"_ns : u"anonymousrootcreated"_ns,
|
||||||
|
CanBubble::eYes, ChromeOnlyDispatch::eYes, Composed::eYes);
|
||||||
|
dispatcher->PostDOMEvent();
|
||||||
|
}
|
||||||
|
|
||||||
nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions) {
|
nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions) {
|
||||||
if (aOptions.mComposed) {
|
if (aOptions.mComposed) {
|
||||||
if (Document* doc = GetComposedDoc()) {
|
if (Document* doc = GetComposedDoc()) {
|
||||||
|
@ -3615,10 +3624,16 @@ ParentObject nsINode::GetParentObject() const {
|
||||||
ParentObject p(OwnerDoc());
|
ParentObject p(OwnerDoc());
|
||||||
// Note that mReflectionScope is a no-op for chrome, and other places where we
|
// Note that mReflectionScope is a no-op for chrome, and other places where we
|
||||||
// don't check this value.
|
// don't check this value.
|
||||||
if (ShouldUseUAWidgetScope(this)) {
|
if (IsInNativeAnonymousSubtree()) {
|
||||||
p.mReflectionScope = ReflectionScope::UAWidget;
|
if (ShouldUseUAWidgetScope(this)) {
|
||||||
} else if (ShouldUseNACScope(this)) {
|
p.mReflectionScope = ReflectionScope::UAWidget;
|
||||||
p.mReflectionScope = ReflectionScope::NAC;
|
} else {
|
||||||
|
MOZ_ASSERT(ShouldUseNACScope(this));
|
||||||
|
p.mReflectionScope = ReflectionScope::NAC;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(!ShouldUseNACScope(this));
|
||||||
|
MOZ_ASSERT(!ShouldUseUAWidgetScope(this));
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1692,6 +1692,8 @@ class nsINode : public mozilla::dom::EventTarget {
|
||||||
*/
|
*/
|
||||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY void FireNodeRemovedForChildren();
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY void FireNodeRemovedForChildren();
|
||||||
|
|
||||||
|
void QueueDevtoolsAnonymousEvent(bool aIsRemove);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mozilla::dom::SVGUseElement* DoGetContainingSVGUseShadowHost() const;
|
mozilla::dom::SVGUseElement* DoGetContainingSVGUseShadowHost() const;
|
||||||
|
|
||||||
|
|
|
@ -71,12 +71,6 @@ class MutationObserverWrapper final : public nsIMutationObserver {
|
||||||
aOldValue);
|
aOldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeAnonymousChildListChange(nsIContent* aContent,
|
|
||||||
bool aIsRemove) override {
|
|
||||||
MOZ_ASSERT(mOwner);
|
|
||||||
mOwner->NativeAnonymousChildListChange(aContent, aIsRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AttributeSetToCurrentValue(mozilla::dom::Element* aElement,
|
void AttributeSetToCurrentValue(mozilla::dom::Element* aElement,
|
||||||
int32_t aNameSpaceID,
|
int32_t aNameSpaceID,
|
||||||
nsAtom* aAttribute) override {
|
nsAtom* aAttribute) override {
|
||||||
|
|
|
@ -840,7 +840,6 @@ skip-if = os == 'mac' || os == 'linux' || headless
|
||||||
skip-if =
|
skip-if =
|
||||||
http3
|
http3
|
||||||
[test_mozMatchesSelector.html]
|
[test_mozMatchesSelector.html]
|
||||||
[test_mutationobserver_anonymous.html]
|
|
||||||
[test_mutationobservers.html]
|
[test_mutationobservers.html]
|
||||||
[test_named_frames.html]
|
[test_named_frames.html]
|
||||||
[test_navigator_cookieEnabled.html]
|
[test_navigator_cookieEnabled.html]
|
||||||
|
|
|
@ -1,265 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1034110
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<title>Test for Bug 1034110</title>
|
|
||||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1034110">Mozilla Bug 1034110</a>
|
|
||||||
<style type="text/css">
|
|
||||||
#pseudo.before::before { content: "before"; }
|
|
||||||
#pseudo.after::after { content: "after"; }
|
|
||||||
</style>
|
|
||||||
<div id="pseudo"></div>
|
|
||||||
<video id="video"></video>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content" style="display: none">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript">
|
|
||||||
|
|
||||||
/** Test for Bug 1034110 **/
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
|
|
||||||
function getWalker(node) {
|
|
||||||
return SpecialPowers.createDOMWalker(node, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFirstChild(parent) {
|
|
||||||
return SpecialPowers.unwrap(getWalker(parent).firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLastChild(parent) {
|
|
||||||
return SpecialPowers.unwrap(getWalker(parent).lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertSamePseudoElement(which, node1, node2) {
|
|
||||||
is(SpecialPowers.wrap(node1).nodeName, "_moz_generated_content_" + which,
|
|
||||||
"Correct pseudo element type");
|
|
||||||
is(node1, node2,
|
|
||||||
"Referencing the same ::after element");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
testOneAdded();
|
|
||||||
};
|
|
||||||
|
|
||||||
function testOneAdded() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 1, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type");
|
|
||||||
is(records[0].target, parent, "Correct target");
|
|
||||||
|
|
||||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
|
|
||||||
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testAddedAndRemoved();
|
|
||||||
});
|
|
||||||
|
|
||||||
SpecialPowers.observeMutationEvents(m, parent, true);
|
|
||||||
parent.className = "before";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAddedAndRemoved() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
let originalBeforeElement = getFirstChild(parent);
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 2, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
|
|
||||||
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
|
|
||||||
is(records[0].target, parent, "Correct target (1)");
|
|
||||||
is(records[1].target, parent, "Correct target (2)");
|
|
||||||
|
|
||||||
// Two records are sent - one for removed and one for added.
|
|
||||||
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
|
|
||||||
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
|
|
||||||
assertSamePseudoElement("before", records[0].removedNodes[0], originalBeforeElement);
|
|
||||||
|
|
||||||
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
|
|
||||||
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testRemoved();
|
|
||||||
});
|
|
||||||
|
|
||||||
SpecialPowers.observeMutationEvents(m, parent, true);
|
|
||||||
parent.className = "after";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testRemoved() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
let originalAfterElement = getLastChild(parent);
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 1, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type");
|
|
||||||
is(records[0].target, parent, "Correct target");
|
|
||||||
|
|
||||||
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
|
|
||||||
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
|
|
||||||
assertSamePseudoElement("after", records[0].removedNodes[0], originalAfterElement);
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testMultipleAdded();
|
|
||||||
});
|
|
||||||
|
|
||||||
SpecialPowers.observeMutationEvents(m, parent, true);
|
|
||||||
parent.className = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMultipleAdded() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 2, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
|
|
||||||
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
|
|
||||||
is(records[0].target, parent, "Correct target (1)");
|
|
||||||
is(records[1].target, parent, "Correct target (2)");
|
|
||||||
|
|
||||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
|
|
||||||
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
|
|
||||||
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testRemovedDueToDisplay();
|
|
||||||
});
|
|
||||||
|
|
||||||
SpecialPowers.observeMutationEvents(m, parent, true);
|
|
||||||
parent.className = "before after";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testRemovedDueToDisplay() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
let originalBeforeElement = getFirstChild(parent);
|
|
||||||
let originalAfterElement = getLastChild(parent);
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 2, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
|
|
||||||
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
|
|
||||||
is(records[0].target, parent, "Correct target (1)");
|
|
||||||
is(records[1].target, parent, "Correct target (2)");
|
|
||||||
|
|
||||||
is(records[0].addedNodes.length, 0, "Shouldn't have got addedNodes");
|
|
||||||
is(records[0].removedNodes.length, 1, "Should have got removedNodes");
|
|
||||||
assertSamePseudoElement("after", records[0].removedNodes[0], originalAfterElement);
|
|
||||||
|
|
||||||
is(records[1].addedNodes.length, 0, "Shouldn't have got addedNodes");
|
|
||||||
is(records[1].removedNodes.length, 1, "Should have got removedNodes");
|
|
||||||
assertSamePseudoElement("before", records[1].removedNodes[0], originalBeforeElement);
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testAddedDueToDisplay();
|
|
||||||
});
|
|
||||||
|
|
||||||
SpecialPowers.observeMutationEvents(m, parent, true);
|
|
||||||
parent.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testAddedDueToDisplay() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 2, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
|
|
||||||
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
|
|
||||||
is(records[0].target, parent, "Correct target (1)");
|
|
||||||
is(records[1].target, parent, "Correct target (2)");
|
|
||||||
|
|
||||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
|
|
||||||
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
|
|
||||||
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testDifferentTargetNoSubtree();
|
|
||||||
});
|
|
||||||
|
|
||||||
SpecialPowers.observeMutationEvents(m, parent, true);
|
|
||||||
parent.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDifferentTargetNoSubtree() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
ok(false,
|
|
||||||
"No mutation should fire when observing on a parent without subtree option.");
|
|
||||||
});
|
|
||||||
SpecialPowers.observeMutationEvents(m, document, true);
|
|
||||||
parent.style.display = "none";
|
|
||||||
|
|
||||||
// Wait for the actual mutation to come through, making sure that
|
|
||||||
// the original observer never fires.
|
|
||||||
var m2 = new MutationObserver(function(records, observer) {
|
|
||||||
ok(!getFirstChild(parent), "Pseudo element has been removed, but no mutation");
|
|
||||||
ok(!getLastChild(parent), "Pseudo element has been removed, but no mutation");
|
|
||||||
observer.disconnect();
|
|
||||||
testSubtree();
|
|
||||||
});
|
|
||||||
SpecialPowers.observeMutationEvents(m2, parent, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSubtree() {
|
|
||||||
let parent = document.getElementById("pseudo");
|
|
||||||
var m = new MutationObserver(function(records, observer) {
|
|
||||||
is(records.length, 2, "Correct number of records");
|
|
||||||
is(records[0].type, "nativeAnonymousChildList", "Correct record type (1)");
|
|
||||||
is(records[1].type, "nativeAnonymousChildList", "Correct record type (2)");
|
|
||||||
is(records[0].target, parent, "Correct target (1)");
|
|
||||||
is(records[1].target, parent, "Correct target (2)");
|
|
||||||
|
|
||||||
is(records[0].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("before", records[0].addedNodes[0], getFirstChild(parent));
|
|
||||||
is(records[0].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
is(records[1].addedNodes.length, 1, "Should have got addedNodes");
|
|
||||||
assertSamePseudoElement("after", records[1].addedNodes[0], getLastChild(parent));
|
|
||||||
is(records[1].removedNodes.length, 0, "Shouldn't have got removedNodes");
|
|
||||||
|
|
||||||
observer.disconnect();
|
|
||||||
testDictionaryWithoutChromePriv();
|
|
||||||
});
|
|
||||||
SpecialPowers.observeMutationEvents(m, document, true, true);
|
|
||||||
parent.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDictionaryWithoutChromePriv()
|
|
||||||
{
|
|
||||||
var m = new MutationObserver(function() {});
|
|
||||||
try {
|
|
||||||
m.observe(document, { childList: true, get nativeAnonymousChildList() { throw "Foo1"; } } );
|
|
||||||
ok(true, "Shouldn't throw!");
|
|
||||||
} catch(ex) {
|
|
||||||
ok(false, "Did throw " + ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
m.observe(document, { childList: true, get animations() { throw "Foo2"; } } );
|
|
||||||
ok(true, "Shouldn't throw!");
|
|
||||||
} catch(ex) {
|
|
||||||
ok(false, "Did throw " + ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
SimpleTest.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -840,10 +840,10 @@ function testAttributeRecordMerging4() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testChromeOnly() {
|
function testChromeOnly() {
|
||||||
// Content can't access nativeAnonymousChildList
|
// Content can't access chromeOnlyNodes
|
||||||
try {
|
try {
|
||||||
var mo = new M(function(records, observer) { });
|
var mo = new M(function(records, observer) { });
|
||||||
mo.observe(div, { nativeAnonymousChildList: true });
|
mo.observe(div, { chromeOnlyNodes: true });
|
||||||
ok(false, "Should have thrown when trying to observe with chrome-only init");
|
ok(false, "Should have thrown when trying to observe with chrome-only init");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ok(true, "Throws when trying to observe with chrome-only init");
|
ok(true, "Throws when trying to observe with chrome-only init");
|
||||||
|
|
|
@ -441,7 +441,7 @@ partial interface Document {
|
||||||
attribute boolean styleSheetChangeEventsEnabled;
|
attribute boolean styleSheetChangeEventsEnabled;
|
||||||
|
|
||||||
[ChromeOnly]
|
[ChromeOnly]
|
||||||
attribute boolean shadowRootAttachedEventEnabled;
|
attribute boolean devToolsAnonymousAndShadowEventsEnabled;
|
||||||
|
|
||||||
[ChromeOnly] readonly attribute DOMString contentLanguage;
|
[ChromeOnly] readonly attribute DOMString contentLanguage;
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,6 @@ dictionary MutationObserverInit {
|
||||||
boolean attributeOldValue;
|
boolean attributeOldValue;
|
||||||
boolean characterDataOldValue;
|
boolean characterDataOldValue;
|
||||||
[ChromeOnly]
|
[ChromeOnly]
|
||||||
boolean nativeAnonymousChildList = false;
|
|
||||||
[ChromeOnly]
|
|
||||||
boolean chromeOnlyNodes = false;
|
boolean chromeOnlyNodes = false;
|
||||||
[ChromeOnly]
|
[ChromeOnly]
|
||||||
boolean animations = false;
|
boolean animations = false;
|
||||||
|
|
|
@ -2161,6 +2161,16 @@ static nsIContent* GetNativeAnonymousSubtreeRoot(nsIContent* aContent) {
|
||||||
|
|
||||||
void PresShell::NativeAnonymousContentRemoved(nsIContent* aAnonContent) {
|
void PresShell::NativeAnonymousContentRemoved(nsIContent* aAnonContent) {
|
||||||
MOZ_ASSERT(aAnonContent->IsRootOfNativeAnonymousSubtree());
|
MOZ_ASSERT(aAnonContent->IsRootOfNativeAnonymousSubtree());
|
||||||
|
mPresContext->EventStateManager()->NativeAnonymousContentRemoved(
|
||||||
|
aAnonContent);
|
||||||
|
#ifdef ACCESSIBILITY
|
||||||
|
if (nsAccessibilityService* accService = GetAccService()) {
|
||||||
|
accService->ContentRemoved(this, aAnonContent);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (mDocument->DevToolsAnonymousAndShadowEventsEnabled()) {
|
||||||
|
aAnonContent->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ true);
|
||||||
|
}
|
||||||
if (nsIContent* root = GetNativeAnonymousSubtreeRoot(mCurrentEventContent)) {
|
if (nsIContent* root = GetNativeAnonymousSubtreeRoot(mCurrentEventContent)) {
|
||||||
if (aAnonContent == root) {
|
if (aAnonContent == root) {
|
||||||
mCurrentEventContent = aAnonContent->GetFlattenedTreeParent();
|
mCurrentEventContent = aAnonContent->GetFlattenedTreeParent();
|
||||||
|
|
|
@ -1896,6 +1896,10 @@ void nsCSSFrameConstructor::CreateGeneratedContentItem(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mDocument->DevToolsAnonymousAndShadowEventsEnabled()) {
|
||||||
|
container->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
|
||||||
|
}
|
||||||
|
|
||||||
// Servo has already eagerly computed the style for the container, so we can
|
// Servo has already eagerly computed the style for the container, so we can
|
||||||
// just stick the style on the element and avoid an additional traversal.
|
// just stick the style on the element and avoid an additional traversal.
|
||||||
//
|
//
|
||||||
|
@ -3999,6 +4003,13 @@ nsresult nsCSSFrameConstructor::GetAnonymousContent(
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aContent.IsEmpty()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool devtoolsEventsEnabled =
|
||||||
|
mDocument->DevToolsAnonymousAndShadowEventsEnabled();
|
||||||
|
|
||||||
MOZ_ASSERT(aParent->IsElement());
|
MOZ_ASSERT(aParent->IsElement());
|
||||||
for (const auto& info : aContent) {
|
for (const auto& info : aContent) {
|
||||||
// get our child's content and set its parent to our content
|
// get our child's content and set its parent to our content
|
||||||
|
@ -4012,6 +4023,10 @@ nsresult nsCSSFrameConstructor::GetAnonymousContent(
|
||||||
content->UnbindFromTree();
|
content->UnbindFromTree();
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (devtoolsEventsEnabled) {
|
||||||
|
content->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some situations where we don't cache anonymous content styles:
|
// Some situations where we don't cache anonymous content styles:
|
||||||
|
|
|
@ -221,7 +221,6 @@ static void SetOrUpdateRectValuedProperty(
|
||||||
void nsIFrame::DestroyAnonymousContent(
|
void nsIFrame::DestroyAnonymousContent(
|
||||||
nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
|
nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
|
||||||
if (nsCOMPtr<nsIContent> content = aContent) {
|
if (nsCOMPtr<nsIContent> content = aContent) {
|
||||||
aPresContext->EventStateManager()->NativeAnonymousContentRemoved(content);
|
|
||||||
aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
|
aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
|
||||||
content->UnbindFromTree();
|
content->UnbindFromTree();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2144,13 +2144,6 @@ export class SpecialPowersChild extends JSWindowActorChild {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
observeMutationEvents(mo, node, nativeAnonymousChildList, subtree) {
|
|
||||||
lazy.WrapPrivileged.unwrap(mo).observe(lazy.WrapPrivileged.unwrap(node), {
|
|
||||||
nativeAnonymousChildList,
|
|
||||||
subtree,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which commands are available can be determined by checking which commands
|
* Which commands are available can be determined by checking which commands
|
||||||
* are registered. See \ref
|
* are registered. See \ref
|
||||||
|
|
|
@ -195,9 +195,6 @@ void nsFormFillController::AttributeWillChange(mozilla::dom::Element* aElement,
|
||||||
nsAtom* aAttribute,
|
nsAtom* aAttribute,
|
||||||
int32_t aModType) {}
|
int32_t aModType) {}
|
||||||
|
|
||||||
void nsFormFillController::NativeAnonymousChildListChange(nsIContent* aContent,
|
|
||||||
bool aIsRemove) {}
|
|
||||||
|
|
||||||
void nsFormFillController::ParentChainChanged(nsIContent* aContent) {}
|
void nsFormFillController::ParentChainChanged(nsIContent* aContent) {}
|
||||||
|
|
||||||
void nsFormFillController::ARIAAttributeDefaultWillChange(
|
void nsFormFillController::ARIAAttributeDefaultWillChange(
|
||||||
|
|
|
@ -58,8 +58,6 @@ void nsMenuGroupOwnerX::NodeWillBeDestroyed(nsINode* aNode) {}
|
||||||
void nsMenuGroupOwnerX::AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID,
|
void nsMenuGroupOwnerX::AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID,
|
||||||
nsAtom* aAttribute, int32_t aModType) {}
|
nsAtom* aAttribute, int32_t aModType) {}
|
||||||
|
|
||||||
void nsMenuGroupOwnerX::NativeAnonymousChildListChange(nsIContent* aContent, bool aIsRemove) {}
|
|
||||||
|
|
||||||
void nsMenuGroupOwnerX::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
|
void nsMenuGroupOwnerX::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
|
||||||
nsAtom* aAttribute, int32_t aModType,
|
nsAtom* aAttribute, int32_t aModType,
|
||||||
const nsAttrValue* aOldValue) {
|
const nsAttrValue* aOldValue) {
|
||||||
|
|
|
@ -707,7 +707,6 @@ STATIC_ATOMS = [
|
||||||
Atom("namespaceUri", "namespace-uri"),
|
Atom("namespaceUri", "namespace-uri"),
|
||||||
Atom("NaN", "NaN"),
|
Atom("NaN", "NaN"),
|
||||||
Atom("n", "n"),
|
Atom("n", "n"),
|
||||||
Atom("nativeAnonymousChildList", "nativeAnonymousChildList"),
|
|
||||||
Atom("nav", "nav"),
|
Atom("nav", "nav"),
|
||||||
Atom("ne", "ne"),
|
Atom("ne", "ne"),
|
||||||
Atom("never", "never"),
|
Atom("never", "never"),
|
||||||
|
|
Загрузка…
Ссылка в новой задаче