diff options
Diffstat (limited to 'dom/base')
-rw-r--r-- | dom/base/CustomElementRegistry.cpp | 24 | ||||
-rw-r--r-- | dom/base/CustomElementRegistry.h | 91 |
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 |