From 53c9b77ed41aebb157012eff5e57cad3a962d18e Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 4 Jan 2020 23:29:10 -0500 Subject: Bug 1315885 - Part 4: Implement callback reaction for custom element reactions. Note: Skipped SyncInvokeReactions since it is removed in CE v1, waste of time. Tag UXP Issue #1344 --- dom/base/CustomElementRegistry.cpp | 160 +++++++++++++------------------------ dom/base/CustomElementRegistry.h | 67 +++++++++------- dom/base/FragmentOrElement.cpp | 6 +- 3 files changed, 99 insertions(+), 134 deletions(-) (limited to 'dom/base') diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 6419c1425..bd26e4ff3 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -92,26 +92,12 @@ CustomElementData::CustomElementData(nsIAtom* aType) CustomElementData::CustomElementData(nsIAtom* aType, State aState) : mType(aType) - , mCurrentCallback(-1) , mElementIsBeingCreated(false) , mCreatedCallbackInvoked(true) - , mAssociatedMicroTask(-1) , mState(aState) { } -void -CustomElementData::RunCallbackQueue() -{ - // Note: It's possible to re-enter this method. - while (static_cast(++mCurrentCallback) < mCallbackQueue.Length()) { - mCallbackQueue[mCurrentCallback]->Call(); - } - - mCallbackQueue.Clear(); - mCurrentCallback = -1; -} - //----------------------------------------------------- // CustomElementRegistry @@ -128,7 +114,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry) for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) { - nsAutoPtr& callbacks = iter.UserData()->mCallbacks; + auto& callbacks = iter.UserData()->mCallbacks; if (callbacks->mAttributeChangedCallback.WasPassed()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, @@ -191,43 +177,6 @@ CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject) nsContentUtils::IsWebComponentsEnabled(); } -/* static */ void -CustomElementRegistry::ProcessTopElementQueue() -{ - MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); - - nsTArray>& stack = *sProcessingStack; - uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr); - - for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) { - // Callback queue may have already been processed in an earlier - // element queue or in an element queue that was popped - // off more recently. - if (stack[i]->mAssociatedMicroTask != -1) { - stack[i]->RunCallbackQueue(); - stack[i]->mAssociatedMicroTask = -1; - } - } - - // If this was actually the base element queue, don't bother trying to pop - // the first "queue" marker (sentinel). - if (firstQueue != 0) { - stack.SetLength(firstQueue); - } else { - // Don't pop sentinel for base element queue. - stack.SetLength(1); - } -} - -/* static */ void -CustomElementRegistry::XPCOMShutdown() -{ - sProcessingStack.reset(); -} - -/* static */ Maybe>> -CustomElementRegistry::sProcessingStack; - CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) , mIsCustomDefinitionRunning(false) @@ -237,12 +186,6 @@ CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow) MOZ_ALWAYS_TRUE(mConstructors.init()); mozilla::HoldJSObjects(this); - - if (!sProcessingStack) { - sProcessingStack.emplace(); - // Add the base queue sentinel to the processing stack. - sProcessingStack->AppendElement((CustomElementData*) nullptr); - } } CustomElementRegistry::~CustomElementRegistry() @@ -346,14 +289,15 @@ CustomElementRegistry::SetupCustomElement(Element* aElement, // Enqueuing the created callback will set the CustomElementData on the // element, causing prototype swizzling to occur in Element::WrapObject. - EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, definition); + // We make it synchronously for createElement/createElementNS in order to + // pass tests. It'll be removed when we deprecate custom elements v0. + // SyncInvokeReactions(nsIDocument::eCreated, aElement, definition); } -void -CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, - Element* aCustomElement, - LifecycleCallbackArgs* aArgs, - CustomElementDefinition* aDefinition) +UniquePtr +CustomElementRegistry::CreateCustomElementCallback( + nsIDocument::ElementCallbackType aType, Element* aCustomElement, + LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition) { RefPtr elementData = aCustomElement->GetCustomElementData(); MOZ_ASSERT(elementData, "CustomElementData should exist"); @@ -372,7 +316,7 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType if (!definition || definition->mLocalName != info->NameAtom()) { // Trying to enqueue a callback for an element that is not // a custom element. We are done, nothing to do. - return; + return nullptr; } } @@ -406,7 +350,7 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType // If there is no such callback, stop. if (!func) { - return; + return nullptr; } if (aType == nsIDocument::eCreated) { @@ -414,54 +358,41 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType } else if (!elementData->mCreatedCallbackInvoked) { // Callbacks other than created callback must not be enqueued // until after the created callback has been invoked. - return; + return nullptr; } // Add CALLBACK to ELEMENT's callback queue. - CustomElementCallback* callback = new CustomElementCallback(aCustomElement, - aType, - func, - elementData); - // Ownership of callback is taken by mCallbackQueue. - elementData->mCallbackQueue.AppendElement(callback); + auto callback = + MakeUnique(aCustomElement, aType, func, elementData); + if (aArgs) { callback->SetArgs(*aArgs); } - if (!elementData->mElementIsBeingCreated) { - CustomElementData* lastData = - sProcessingStack->SafeLastElement(nullptr); - - // A new element queue needs to be pushed if the queue at the - // top of the stack is associated with another microtask level. - bool shouldPushElementQueue = - (!lastData || lastData->mAssociatedMicroTask < - static_cast(nsContentUtils::MicroTaskLevel())); - - // Push a new element queue onto the processing stack when appropriate - // (when we enter a new microtask). - if (shouldPushElementQueue) { - // Push a sentinel value on the processing stack to mark the - // boundary between the element queues. - sProcessingStack->AppendElement((CustomElementData*) nullptr); - } + return Move(callback); +} - sProcessingStack->AppendElement(elementData); - elementData->mAssociatedMicroTask = - static_cast(nsContentUtils::MicroTaskLevel()); - - // Add a script runner to pop and process the element queue at - // the top of the processing stack. - if (shouldPushElementQueue) { - // Lifecycle callbacks enqueued by user agent implementation - // should be invoked prior to returning control back to script. - // Create a script runner to process the top of the processing - // stack as soon as it is safe to run script. - nsCOMPtr runnable = - NS_NewRunnableFunction(&CustomElementRegistry::ProcessTopElementQueue); - nsContentUtils::AddScriptRunner(runnable); - } +void +CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, + Element* aCustomElement, + LifecycleCallbackArgs* aArgs, + CustomElementDefinition* aDefinition) +{ + auto callback = + CreateCustomElementCallback(aType, aCustomElement, aArgs, aDefinition); + if (!callback) { + return; } + + DocGroup* docGroup = mWindow->GetDocGroup(); + if (!docGroup) { + return; + } + + CustomElementReactionsStack* reactionsStack = + docGroup->CustomElementReactionsStack(); + reactionsStack->EnqueueCallbackReaction(this, aCustomElement, aDefinition, + Move(callback)); } void @@ -933,6 +864,16 @@ CustomElementReactionsStack::EnqueueUpgradeReaction(CustomElementRegistry* aRegi Enqueue(aElement, new CustomElementUpgradeReaction(aRegistry, aDefinition)); } +void +CustomElementReactionsStack::EnqueueCallbackReaction(CustomElementRegistry* aRegistry, + Element* aElement, + CustomElementDefinition* aDefinition, + UniquePtr aCustomElementCallback) +{ + Enqueue(aElement, new CustomElementCallbackReaction(aRegistry, aDefinition, + Move(aCustomElementCallback))); +} + void CustomElementReactionsStack::Enqueue(Element* aElement, CustomElementReaction* aReaction) @@ -1026,5 +967,14 @@ CustomElementUpgradeReaction::Invoke(Element* aElement) mRegistry->Upgrade(aElement, mDefinition); } +//----------------------------------------------------- +// CustomElementCallbackReaction + +/* virtual */ void +CustomElementCallbackReaction::Invoke(Element* aElement) +{ + mCustomElementCallback->Call(); +} + } // namespace dom } // namespace mozilla diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h index cb7bd67a5..33f2a95ff 100644 --- a/dom/base/CustomElementRegistry.h +++ b/dom/base/CustomElementRegistry.h @@ -84,23 +84,14 @@ struct CustomElementData explicit CustomElementData(nsIAtom* aType); CustomElementData(nsIAtom* aType, State aState); - // Objects in this array are transient and empty after each microtask - // checkpoint. - nsTArray> mCallbackQueue; // Custom element type, for