mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
Bug 1409976 - Add slotchange event
* Add support for `slotchange` event * Signal `slotchange` when slot's assigned nodes changes Tag #1375
This commit is contained in:
parent
907e210445
commit
af22b5cba5
9 changed files with 174 additions and 160 deletions
|
|
@ -6,10 +6,13 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsDOMMutationObserver.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
AutoTArray<RefPtr<DocGroup>, 2>* DocGroup::sPendingDocGroups = nullptr;
|
||||
|
||||
/* static */ void
|
||||
DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
|
||||
{
|
||||
|
|
@ -54,5 +57,23 @@ DocGroup::~DocGroup()
|
|||
|
||||
NS_IMPL_ISUPPORTS(DocGroup, nsISupports)
|
||||
|
||||
void
|
||||
DocGroup::SignalSlotChange(const HTMLSlotElement* aSlot)
|
||||
{
|
||||
if (mSignalSlotList.Contains(aSlot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mSignalSlotList.AppendElement(const_cast<HTMLSlotElement*>(aSlot));
|
||||
|
||||
if (!sPendingDocGroups) {
|
||||
// Queue a mutation observer compound microtask.
|
||||
nsDOMMutationObserver::QueueMutationObserverMicroTask();
|
||||
sPendingDocGroups = new AutoTArray<RefPtr<DocGroup>, 2>;
|
||||
}
|
||||
|
||||
sPendingDocGroups->AppendElement(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/CustomElementRegistry.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
@ -73,6 +74,23 @@ public:
|
|||
return mDocuments.end();
|
||||
}
|
||||
|
||||
// Append aSlot to the list of signal slot list, if it's not in it already
|
||||
// list, and queue a mutation observer microtask.
|
||||
void SignalSlotChange(const mozilla::dom::HTMLSlotElement* aSlot);
|
||||
|
||||
const nsTArray<RefPtr<HTMLSlotElement>>& SignalSlotList() const
|
||||
{
|
||||
return mSignalSlotList;
|
||||
}
|
||||
|
||||
void ClearSignalSlotList()
|
||||
{
|
||||
mSignalSlotList.Clear();
|
||||
}
|
||||
|
||||
// List of DocGroups that has non-empty signal slot list.
|
||||
static AutoTArray<RefPtr<DocGroup>, 2>* sPendingDocGroups;
|
||||
|
||||
private:
|
||||
DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
|
||||
~DocGroup();
|
||||
|
|
@ -81,6 +99,7 @@ private:
|
|||
RefPtr<TabGroup> mTabGroup;
|
||||
nsTArray<nsIDocument*> mDocuments;
|
||||
RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
|
||||
nsTArray<RefPtr<HTMLSlotElement>> mSignalSlotList;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
|||
return;
|
||||
}
|
||||
|
||||
bool doEnqueueSlotChange = false;
|
||||
if (oldSlot && oldSlot != currentSlot) {
|
||||
// Move assigned nodes from old slot to new slot.
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
|
||||
|
|
@ -142,6 +143,12 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
|||
|
||||
oldSlot->RemoveAssignedNode(assignedNode);
|
||||
currentSlot->AppendAssignedNode(assignedNode);
|
||||
doEnqueueSlotChange = true;
|
||||
}
|
||||
|
||||
if (doEnqueueSlotChange) {
|
||||
oldSlot->EnqueueSlotChangeEvent();
|
||||
currentSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
} else {
|
||||
// Otherwise add appropriate nodes to this slot from the host.
|
||||
|
|
@ -152,8 +159,13 @@ ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
|
|||
child->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
|
||||
if (child->IsSlotable() && slotName.Equals(name)) {
|
||||
currentSlot->AppendAssignedNode(child);
|
||||
doEnqueueSlotChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (doEnqueueSlotChange) {
|
||||
currentSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,8 +183,13 @@ ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot)
|
|||
if (currentSlots->Length() == 1) {
|
||||
MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
|
||||
mSlotMap.Remove(name);
|
||||
aSlot->ClearAssignedNodes();
|
||||
|
||||
if (aSlot->AssignedNodes().Length() > 0) {
|
||||
aSlot->ClearAssignedNodes();
|
||||
aSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
} else {
|
||||
bool doEnqueueSlotChange = false;
|
||||
bool doReplaceSlot = currentSlots->ElementAt(0) == aSlot;
|
||||
currentSlots->RemoveElement(aSlot);
|
||||
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
|
||||
|
|
@ -186,6 +203,12 @@ ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot)
|
|||
|
||||
aSlot->RemoveAssignedNode(assignedNode);
|
||||
replacementSlot->AppendAssignedNode(assignedNode);
|
||||
doEnqueueSlotChange = true;
|
||||
}
|
||||
|
||||
if (doEnqueueSlotChange) {
|
||||
aSlot->EnqueueSlotChangeEvent();
|
||||
replacementSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -421,6 +444,12 @@ ShadowRoot::MaybeReassignElement(Element* aElement,
|
|||
const HTMLSlotElement* newSlot = AssignSlotFor(aElement);
|
||||
|
||||
if (oldSlot != newSlot) {
|
||||
if (oldSlot) {
|
||||
oldSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
if (newSlot) {
|
||||
newSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -587,11 +616,24 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!aChild->IsSlotable() || aContainer != GetHost()) {
|
||||
if (!aChild->IsSlotable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AssignSlotFor(aChild);
|
||||
if (aContainer && aContainer == GetHost()) {
|
||||
if (const HTMLSlotElement* slot = AssignSlotFor(aChild)) {
|
||||
slot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If parent's root is a shadow root, and parent is a slot whose assigned
|
||||
// nodes is the empty list, then run signal a slot change for parent.
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aContainer);
|
||||
if (slot && slot->GetContainingShadow() == this &&
|
||||
slot->AssignedNodes().IsEmpty()) {
|
||||
slot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -608,13 +650,26 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!aChild->IsSlotable() || aContainer != GetHost()) {
|
||||
if (!aChild->IsSlotable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString slotName;
|
||||
aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
|
||||
UnassignSlotFor(aChild, slotName);
|
||||
if (aContainer && aContainer == GetHost()) {
|
||||
nsAutoString slotName;
|
||||
aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
|
||||
if (const HTMLSlotElement* slot = UnassignSlotFor(aChild, slotName)) {
|
||||
slot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If parent's root is a shadow root, and parent is a slot whose assigned
|
||||
// nodes is the empty list, then run signal a slot change for parent.
|
||||
HTMLSlotElement* slot = HTMLSlotElement::FromContentOrNull(aContainer);
|
||||
if (slot && slot->GetContainingShadow() == this &&
|
||||
slot->AssignedNodes().IsEmpty()) {
|
||||
slot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "mozilla/dom/Animation.h"
|
||||
#include "mozilla/dom/KeyframeEffectReadOnly.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
|
|
@ -609,6 +610,28 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/* static */ void
|
||||
nsDOMMutationObserver::QueueMutationObserverMicroTask()
|
||||
{
|
||||
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
|
||||
if (!ccjs) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<MutationObserverMicroTask> momt =
|
||||
new MutationObserverMicroTask();
|
||||
ccjs->DispatchMicroTaskRunnable(momt.forget());
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation& aAso)
|
||||
{
|
||||
if (sScheduledMutationObservers ||
|
||||
mozilla::dom::DocGroup::sPendingDocGroups) {
|
||||
HandleMutationsInternal(aAso);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMMutationObserver::RescheduleForRun()
|
||||
{
|
||||
|
|
@ -887,7 +910,23 @@ nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso)
|
|||
{
|
||||
nsTArray<RefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
|
||||
|
||||
while (sScheduledMutationObservers) {
|
||||
// Let signalList be a copy of unit of related similar-origin browsing
|
||||
// contexts' signal slot list.
|
||||
nsTArray<RefPtr<HTMLSlotElement>> signalList;
|
||||
if (DocGroup::sPendingDocGroups) {
|
||||
for (uint32_t i = 0; i < DocGroup::sPendingDocGroups->Length(); ++i) {
|
||||
DocGroup* docGroup = DocGroup::sPendingDocGroups->ElementAt(i);
|
||||
signalList.AppendElements(docGroup->SignalSlotList());
|
||||
|
||||
// Empty unit of related similar-origin browsing contexts' signal slot
|
||||
// list.
|
||||
docGroup->ClearSignalSlotList();
|
||||
}
|
||||
delete DocGroup::sPendingDocGroups;
|
||||
DocGroup::sPendingDocGroups = nullptr;
|
||||
}
|
||||
|
||||
if (sScheduledMutationObservers) {
|
||||
AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* observers =
|
||||
sScheduledMutationObservers;
|
||||
sScheduledMutationObservers = nullptr;
|
||||
|
|
@ -917,6 +956,11 @@ nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso)
|
|||
delete suppressedObservers;
|
||||
suppressedObservers = nullptr;
|
||||
}
|
||||
|
||||
// Fire slotchange event for each slot in signalList.
|
||||
for (uint32_t i = 0; i < signalList.Length(); ++i) {
|
||||
signalList[i]->FireSlotChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
nsDOMMutationRecord*
|
||||
|
|
|
|||
|
|
@ -552,12 +552,9 @@ public:
|
|||
}
|
||||
|
||||
// static methods
|
||||
static void HandleMutations(mozilla::AutoSlowOperation& aAso)
|
||||
{
|
||||
if (sScheduledMutationObservers) {
|
||||
HandleMutationsInternal(aAso);
|
||||
}
|
||||
}
|
||||
static void QueueMutationObserverMicroTask();
|
||||
|
||||
static void HandleMutations(mozilla::AutoSlowOperation& aAso);
|
||||
|
||||
static bool AllScheduledMutationObserversAreSuppressed()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/dom/HTMLSlotElementBinding.h"
|
||||
#include "mozilla/dom/HTMLUnknownElement.h"
|
||||
|
|
@ -204,6 +205,26 @@ HTMLSlotElement::ClearAssignedNodes()
|
|||
mAssignedNodes.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLSlotElement::EnqueueSlotChangeEvent() const
|
||||
{
|
||||
DocGroup* docGroup = OwnerDoc()->GetDocGroup();
|
||||
if (!docGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
docGroup->SignalSlotChange(this);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLSlotElement::FireSlotChangeEvent()
|
||||
{
|
||||
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
|
||||
static_cast<nsIContent*>(this),
|
||||
NS_LITERAL_STRING("slotchange"), true,
|
||||
false);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
HTMLSlotElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -62,6 +62,9 @@ public:
|
|||
void RemoveAssignedNode(nsINode* aNode);
|
||||
void ClearAssignedNodes();
|
||||
|
||||
void EnqueueSlotChangeEvent() const;
|
||||
void FireSlotChangeEvent();
|
||||
|
||||
protected:
|
||||
virtual ~HTMLSlotElement();
|
||||
virtual JSObject*
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
[slotchange-event.html]
|
||||
type: testharness
|
||||
expected: ERROR
|
||||
[slotchange event must fire on a default slot element inside an open shadow root in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a default slot element inside a closed shadow root in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a default slot element inside an open shadow root not in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a default slot element inside a closed shadow root not in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a named slot element insidean open shadow root in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a named slot element insidea closed shadow root in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a named slot element insidean open shadow root not in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a named slot element insidea closed shadow root not in a document]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside an open shadow root in a document when another slot's assigned nodes change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside a closed shadow root in a document when another slot's assigned nodes change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside an open shadow root not in a document when another slot's assigned nodes change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside a closed shadow root not in a document when another slot's assigned nodes change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside an open shadow root in a document when the shadow host was mutated before the slot was inserted or after the slot was removed]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside a closed shadow root in a document when the shadow host was mutated before the slot was inserted or after the slot was removed]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside an open shadow root not in a document when the shadow host was mutated before the slot was inserted or after the slot was removed]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must not fire on a slot element inside a closed shadow root not in a document when the shadow host was mutated before the slot was inserted or after the slot was removed]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside an open shadow root in a document even if the slot was removed immediately after the assigned nodes were mutated]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside a closed shadow root in a document even if the slot was removed immediately after the assigned nodes were mutated]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside an open shadow root not in a document even if the slot was removed immediately after the assigned nodes were mutated]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside a closed shadow root not in a document even if the slot was removed immediately after the assigned nodes were mutated]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside an open shadow root in a document when innerHTML modifies the children of the shadow host]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside a closed shadow root in a document when innerHTML modifies the children of the shadow host]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside an open shadow root not in a document when innerHTML modifies the children of the shadow host]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside a closed shadow root not in a document when innerHTML modifies the children of the shadow host]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside an open shadow root in a document when nested slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside a closed shadow root in a document when nested slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside an open shadow root not in a document when nested slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire on a slot element inside a closed shadow root not in a document when nested slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire at the end of current microtask after mutation observers are invoked inside an open shadow root in a document when slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire at the end of current microtask after mutation observers are invoked inside a closed shadow root in a document when slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire at the end of current microtask after mutation observers are invoked inside an open shadow root not in a document when slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event must fire at the end of current microtask after mutation observers are invoked inside a closed shadow root not in a document when slots's contents change]
|
||||
expected: FAIL
|
||||
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
[slotchange.html]
|
||||
type: testharness
|
||||
[slotchange event: Append a child to a host.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Remove a child from a host.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Remove a child before adding an event listener.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Change slot= attribute to make it un-assigned.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Change slot's name= attribute so that none is assigned.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Change slot= attribute to make it assigned.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Change slot's name= attribute so that a node is assigned to the slot.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Add a fallback content.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Remove a fallback content.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Add a fallback content to nested slots.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Remove a fallback content from nested slots.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Insert a slot before an existing slot.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Remove a preceding slot.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: A slot is assigned to another slot.]
|
||||
expected: FAIL
|
||||
|
||||
[slotchange event: Even if distributed nodes do not change, slotchange should be fired if assigned nodes are changed.]
|
||||
expected: FAIL
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue