Bug 1421544 - Lazy push/pop CustomElementReactionsStack entry;

Tag UXP Issue #1344
This commit is contained in:
Gaming4JC 2020-01-25 08:59:17 -05:00 committed by Roy Tam
commit a6339bef90
2 changed files with 96 additions and 19 deletions

View file

@ -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);

View file

@ -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