diff options
author | Matt A. Tobin <email@mattatobin.com> | 2020-04-17 07:35:48 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2020-04-17 07:35:48 -0400 |
commit | 3508e79b1fe7fc928eed2f3c7bf2d628c53fbf17 (patch) | |
tree | c793eb598816afa68a560c4224b06ca15b8ad55c /dom | |
parent | f164962a3dc53f4823dfff6c77e7972c72d4c61f (diff) | |
download | UXP-3508e79b1fe7fc928eed2f3c7bf2d628c53fbf17.tar UXP-3508e79b1fe7fc928eed2f3c7bf2d628c53fbf17.tar.gz UXP-3508e79b1fe7fc928eed2f3c7bf2d628c53fbf17.tar.lz UXP-3508e79b1fe7fc928eed2f3c7bf2d628c53fbf17.tar.xz UXP-3508e79b1fe7fc928eed2f3c7bf2d628c53fbf17.zip |
Bug 1409976 - Add `slotchange` event
* Add support for `slotchange` event
* Signal `slotchange` when slot's assigned nodes changes
Tag #1375
Diffstat (limited to 'dom')
-rw-r--r-- | dom/base/DocGroup.cpp | 21 | ||||
-rw-r--r-- | dom/base/DocGroup.h | 19 | ||||
-rw-r--r-- | dom/base/ShadowRoot.cpp | 69 | ||||
-rw-r--r-- | dom/base/nsDOMMutationObserver.cpp | 46 | ||||
-rw-r--r-- | dom/base/nsDOMMutationObserver.h | 9 | ||||
-rw-r--r-- | dom/html/HTMLSlotElement.cpp | 21 | ||||
-rw-r--r-- | dom/html/HTMLSlotElement.h | 3 |
7 files changed, 174 insertions, 14 deletions
diff --git a/dom/base/DocGroup.cpp b/dom/base/DocGroup.cpp index 30c058f0c..826a71812 100644 --- a/dom/base/DocGroup.cpp +++ b/dom/base/DocGroup.cpp @@ -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); +} + } } diff --git a/dom/base/DocGroup.h b/dom/base/DocGroup.h index 5b8f627cc..7a5a99dce 100644 --- a/dom/base/DocGroup.h +++ b/dom/base/DocGroup.h @@ -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 diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp index e7f7ae93c..12f35c197 100644 --- a/dom/base/ShadowRoot.cpp +++ b/dom/base/ShadowRoot.cpp @@ -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 diff --git a/dom/base/nsDOMMutationObserver.cpp b/dom/base/nsDOMMutationObserver.cpp index 4c4731c11..fbd64dc20 100644 --- a/dom/base/nsDOMMutationObserver.cpp +++ b/dom/base/nsDOMMutationObserver.cpp @@ -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* diff --git a/dom/base/nsDOMMutationObserver.h b/dom/base/nsDOMMutationObserver.h index a8babc603..d6bdfb3e0 100644 --- a/dom/base/nsDOMMutationObserver.h +++ b/dom/base/nsDOMMutationObserver.h @@ -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() { diff --git a/dom/html/HTMLSlotElement.cpp b/dom/html/HTMLSlotElement.cpp index b13729a09..18b8ef87b 100644 --- a/dom/html/HTMLSlotElement.cpp +++ b/dom/html/HTMLSlotElement.cpp @@ -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) { diff --git a/dom/html/HTMLSlotElement.h b/dom/html/HTMLSlotElement.h index 4f8546200..0494a654e 100644 --- a/dom/html/HTMLSlotElement.h +++ b/dom/html/HTMLSlotElement.h @@ -62,6 +62,9 @@ public: void RemoveAssignedNode(nsINode* aNode); void ClearAssignedNodes(); + void EnqueueSlotChangeEvent() const; + void FireSlotChangeEvent(); + protected: virtual ~HTMLSlotElement(); virtual JSObject* |