summaryrefslogtreecommitdiffstats
path: root/dom/base
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base')
-rw-r--r--dom/base/CustomElementRegistry.cpp24
-rw-r--r--dom/base/CustomElementRegistry.h91
2 files changed, 96 insertions, 19 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
index 4a803b710..4fb18381f 100644
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -951,18 +951,24 @@ CustomElementRegistry::Upgrade(Element* aElement,
void
CustomElementReactionsStack::CreateAndPushElementQueue()
{
+ MOZ_ASSERT(mRecursionDepth);
+ MOZ_ASSERT(!mIsElementQueuePushedForCurrentRecursionDepth);
+
// Push a new element queue onto the custom element reactions stack.
mReactionsStack.AppendElement(MakeUnique<ElementQueue>());
+ mIsElementQueuePushedForCurrentRecursionDepth = true;
}
void
CustomElementReactionsStack::PopAndInvokeElementQueue()
{
- // Pop the element queue from the custom element reactions stack,
- // and invoke custom element reactions in that queue.
+ MOZ_ASSERT(mRecursionDepth);
+ MOZ_ASSERT(mIsElementQueuePushedForCurrentRecursionDepth);
MOZ_ASSERT(!mReactionsStack.IsEmpty(),
"Reaction stack shouldn't be empty");
+ // Pop the element queue from the custom element reactions stack,
+ // and invoke custom element reactions in that queue.
const uint32_t lastIndex = mReactionsStack.Length() - 1;
ElementQueue* elementQueue = mReactionsStack.ElementAt(lastIndex).get();
// Check element queue size in order to reduce function call overhead.
@@ -986,6 +992,7 @@ CustomElementReactionsStack::PopAndInvokeElementQueue()
"reactions created by InvokeReactions() should be consumed and removed");
mReactionsStack.RemoveElementAt(lastIndex);
+ mIsElementQueuePushedForCurrentRecursionDepth = false;
}
void
@@ -1009,8 +1016,15 @@ CustomElementReactionsStack::Enqueue(Element* aElement,
RefPtr<CustomElementData> elementData = aElement->GetCustomElementData();
MOZ_ASSERT(elementData, "CustomElementData should exist");
- // Add element to the current element queue.
- if (!mReactionsStack.IsEmpty()) {
+ if (mRecursionDepth) {
+ // If the element queue is not created for current recursion depth, create
+ // and push an element queue to reactions stack first.
+ if (!mIsElementQueuePushedForCurrentRecursionDepth) {
+ CreateAndPushElementQueue();
+ }
+
+ MOZ_ASSERT(!mReactionsStack.IsEmpty());
+ // Add element to the current element queue.
mReactionsStack.LastElement()->AppendElement(aElement);
elementData->mReactionQueue.AppendElement(aReaction);
return;
@@ -1018,6 +1032,8 @@ CustomElementReactionsStack::Enqueue(Element* aElement,
// If the custom element reactions stack is empty, then:
// Add element to the backup element queue.
+ MOZ_ASSERT(mReactionsStack.IsEmpty(),
+ "custom element reactions stack should be empty");
MOZ_ASSERT(!aReaction->IsUpgradeReaction(),
"Upgrade reaction should not be scheduled to backup queue");
mBackupQueue.AppendElement(aElement);
diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h
index 8cea11d1f..01c32a595 100644
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -258,6 +258,8 @@ public:
CustomElementReactionsStack()
: mIsBackupQueueProcessing(false)
+ , mRecursionDepth(0)
+ , mIsElementQueuePushedForCurrentRecursionDepth(false)
{
}
@@ -282,18 +284,67 @@ public:
void EnqueueCallbackReaction(Element* aElement,
UniquePtr<CustomElementCallback> aCustomElementCallback);
- // [CEReactions] Before executing the algorithm's steps
- // Push a new element queue onto the custom element reactions stack.
- void CreateAndPushElementQueue();
+ /**
+ * [CEReactions] Before executing the algorithm's steps.
+ * Increase the current recursion depth, and the element queue is pushed
+ * lazily when we really enqueue reactions.
+ *
+ * @return true if the element queue is pushed for "previous" recursion depth.
+ */
+ bool EnterCEReactions()
+ {
+ bool temp = mIsElementQueuePushedForCurrentRecursionDepth;
+ mRecursionDepth++;
+ // The is-element-queue-pushed flag is initially false when entering a new
+ // recursion level. The original value will be cached in AutoCEReaction
+ // and restored after leaving this recursion level.
+ mIsElementQueuePushedForCurrentRecursionDepth = false;
+ return temp;
+ }
- // [CEReactions] After executing the algorithm's steps
- // Pop the element queue from the custom element reactions stack,
- // and invoke custom element reactions in that queue.
- void PopAndInvokeElementQueue();
+ /**
+ * [CEReactions] After executing the algorithm's steps.
+ * Pop and invoke the element queue if it is created and pushed for current
+ * recursion depth, then decrease the current recursion depth.
+ *
+ * @param aCx JSContext used for handling exception thrown by algorithm's
+ * steps, this could be a nullptr.
+ * aWasElementQueuePushed used for restoring status after leaving
+ * current recursion.
+ */
+ void LeaveCEReactions(JSContext* aCx, bool aWasElementQueuePushed)
+ {
+ MOZ_ASSERT(mRecursionDepth);
+
+ if (mIsElementQueuePushedForCurrentRecursionDepth) {
+ Maybe<JS::AutoSaveExceptionState> ases;
+ if (aCx) {
+ ases.emplace(aCx);
+ }
+ PopAndInvokeElementQueue();
+ }
+ mRecursionDepth--;
+ // Restore the is-element-queue-pushed flag cached in AutoCEReaction when
+ // leaving the recursion level.
+ mIsElementQueuePushedForCurrentRecursionDepth = aWasElementQueuePushed;
+
+ MOZ_ASSERT_IF(!mRecursionDepth, mReactionsStack.IsEmpty());
+ }
private:
~CustomElementReactionsStack() {};
+ /**
+ * Push a new element queue onto the custom element reactions stack.
+ */
+ void CreateAndPushElementQueue();
+
+ /**
+ * Pop the element queue from the custom element reactions stack, and invoke
+ * custom element reactions in that queue.
+ */
+ void PopAndInvokeElementQueue();
+
// The choice of 8 for the auto size here is based on gut feeling.
AutoTArray<UniquePtr<ElementQueue>, 8> mReactionsStack;
ElementQueue mBackupQueue;
@@ -310,6 +361,13 @@ private:
void Enqueue(Element* aElement, CustomElementReaction* aReaction);
+ // Current [CEReactions] recursion depth.
+ uint32_t mRecursionDepth;
+ // True if the element queue is pushed into reaction stack for current
+ // recursion depth. This will be cached in AutoCEReaction when entering a new
+ // CEReaction recursion and restored after leaving the recursion.
+ bool mIsElementQueuePushedForCurrentRecursionDepth;
+
private:
class BackupQueueMicroTask final : public mozilla::MicroTaskRunnable {
public:
@@ -477,19 +535,22 @@ class MOZ_RAII AutoCEReaction final {
// exception during the lifetime of the AutoCEReaction.
AutoCEReaction(CustomElementReactionsStack* aReactionsStack, JSContext* aCx)
: mReactionsStack(aReactionsStack)
- , mCx(aCx) {
- mReactionsStack->CreateAndPushElementQueue();
+ , mCx(aCx)
+ {
+ mIsElementQueuePushedForPreviousRecursionDepth =
+ mReactionsStack->EnterCEReactions();
}
- ~AutoCEReaction() {
- Maybe<JS::AutoSaveExceptionState> ases;
- if (mCx) {
- ases.emplace(mCx);
- }
- mReactionsStack->PopAndInvokeElementQueue();
+
+ ~AutoCEReaction()
+ {
+ mReactionsStack->LeaveCEReactions(
+ mCx, mIsElementQueuePushedForPreviousRecursionDepth);
}
+
private:
RefPtr<CustomElementReactionsStack> mReactionsStack;
JSContext* mCx;
+ bool mIsElementQueuePushedForPreviousRecursionDepth;
};
} // namespace dom