diff options
author | Gaming4JC <g4jc@hyperbola.info> | 2020-01-04 23:29:10 -0500 |
---|---|---|
committer | Gaming4JC <g4jc@hyperbola.info> | 2020-01-26 15:50:22 -0500 |
commit | 53c9b77ed41aebb157012eff5e57cad3a962d18e (patch) | |
tree | 774cec41db2f86d1621497b7df20899bc9255622 /dom | |
parent | e6733c9278d0e9687be83de5b5f409a43653fbee (diff) | |
download | UXP-53c9b77ed41aebb157012eff5e57cad3a962d18e.tar UXP-53c9b77ed41aebb157012eff5e57cad3a962d18e.tar.gz UXP-53c9b77ed41aebb157012eff5e57cad3a962d18e.tar.lz UXP-53c9b77ed41aebb157012eff5e57cad3a962d18e.tar.xz UXP-53c9b77ed41aebb157012eff5e57cad3a962d18e.zip |
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
Diffstat (limited to 'dom')
-rw-r--r-- | dom/base/CustomElementRegistry.cpp | 160 | ||||
-rw-r--r-- | dom/base/CustomElementRegistry.h | 67 | ||||
-rw-r--r-- | dom/base/FragmentOrElement.cpp | 6 | ||||
-rw-r--r-- | dom/tests/mochitest/webcomponents/test_document_register_stack.html | 4 |
4 files changed, 101 insertions, 136 deletions
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<uint32_t>(++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<LifecycleCallbacks>& 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<RefPtr<CustomElementData>>& 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<nsTArray<RefPtr<CustomElementData>>> -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<CustomElementCallback> +CustomElementRegistry::CreateCustomElementCallback( + nsIDocument::ElementCallbackType aType, Element* aCustomElement, + LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition) { RefPtr<CustomElementData> 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<CustomElementCallback>(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<int32_t>(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<int32_t>(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<nsIRunnable> 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 @@ -934,6 +865,16 @@ CustomElementReactionsStack::EnqueueUpgradeReaction(CustomElementRegistry* aRegi } void +CustomElementReactionsStack::EnqueueCallbackReaction(CustomElementRegistry* aRegistry, + Element* aElement, + CustomElementDefinition* aDefinition, + UniquePtr<CustomElementCallback> 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<nsAutoPtr<CustomElementCallback>> mCallbackQueue; // Custom element type, for <button is="x-button"> or <x-button> // this would be x-button. nsCOMPtr<nsIAtom> mType; - // The callback that is next to be processed upon calling RunCallbackQueue. - int32_t mCurrentCallback; // Element is being created flag as described in the custom elements spec. bool mElementIsBeingCreated; // Flag to determine if the created callback has been invoked, thus it // determines if other callbacks can be enqueued. bool mCreatedCallbackInvoked; - // The microtask level associated with the callbacks in the callback queue, - // it is used to determine if a new queue needs to be pushed onto the - // processing stack. - int32_t mAssociatedMicroTask; // Custom element state as described in the custom element spec. State mState; // custom element reaction queue as described in the custom element spec. @@ -109,10 +100,7 @@ struct CustomElementData // appended, removed, or replaced. // There are 3 reactions in reaction queue when doing upgrade operation, // e.g., create an element, insert a node. - AutoTArray<nsAutoPtr<CustomElementReaction>, 3> mReactionQueue; - - // Empties the callback queue. - void RunCallbackQueue(); + AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue; private: virtual ~CustomElementData() {} @@ -142,7 +130,7 @@ struct CustomElementDefinition JS::Heap<JSObject *> mPrototype; // The lifecycle callbacks to call for this custom element. - nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks; + UniquePtr<mozilla::dom::LifecycleCallbacks> mCallbacks; // A construction stack. // TODO: Bug 1287348 - Implement construction stack for upgrading an element @@ -163,10 +151,13 @@ public: : mRegistry(aRegistry) , mDefinition(aDefinition) { - }; + } virtual ~CustomElementReaction() = default; virtual void Invoke(Element* aElement) = 0; + virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const + { + } protected: CustomElementRegistry* mRegistry; @@ -186,6 +177,27 @@ private: virtual void Invoke(Element* aElement) override; }; +class CustomElementCallbackReaction final : public CustomElementReaction +{ + public: + CustomElementCallbackReaction(CustomElementRegistry* aRegistry, + CustomElementDefinition* aDefinition, + UniquePtr<CustomElementCallback> aCustomElementCallback) + : CustomElementReaction(aRegistry, aDefinition) + , mCustomElementCallback(Move(aCustomElementCallback)) + { + } + + virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const override + { + mCustomElementCallback->Traverse(aCb); + } + + private: + virtual void Invoke(Element* aElement) override; + UniquePtr<CustomElementCallback> mCustomElementCallback; +}; + // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack class CustomElementReactionsStack { @@ -211,6 +223,15 @@ public: Element* aElement, CustomElementDefinition* aDefinition); + /** + * Enqueue a custom element callback reaction + * https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-callback-reaction + */ + void EnqueueCallbackReaction(CustomElementRegistry* aRegistry, + Element* aElement, + CustomElementDefinition* aDefinition, + UniquePtr<CustomElementCallback> aCustomElementCallback); + // [CEReactions] Before executing the algorithm's steps // Push a new element queue onto the custom element reactions stack. void CreateAndPushElementQueue(); @@ -276,10 +297,6 @@ public: static bool IsCustomElementEnabled(JSContext* aCx = nullptr, JSObject* aObject = nullptr); - static void ProcessTopElementQueue(); - - static void XPCOMShutdown(); - explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow); /** @@ -312,6 +329,10 @@ public: private: ~CustomElementRegistry(); + UniquePtr<CustomElementCallback> CreateCustomElementCallback( + nsIDocument::ElementCallbackType aType, Element* aCustomElement, + LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition); + /** * Registers an unresolved custom element that is a candidate for * upgrade when the definition is registered via registerElement. @@ -358,14 +379,6 @@ private: nsCOMPtr<nsPIDOMWindowInner> mWindow; - // Array representing the processing stack in the custom elements - // specification. The processing stack is conceptually a stack of - // element queues. Each queue is represented by a sequence of - // CustomElementData in this array, separated by nullptr that - // represent the boundaries of the items in the stack. The first - // queue in the stack is the base element queue. - static mozilla::Maybe<nsTArray<RefPtr<CustomElementData>>> sProcessingStack; - // It is used to prevent reentrant invocations of element definition. bool mIsCustomDefinitionRunning; diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 5755fb817..e341ae315 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -584,8 +584,10 @@ FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb) if (mExtendedSlots->mCustomElementData) { for (uint32_t i = 0; - i < mExtendedSlots->mCustomElementData->mCallbackQueue.Length(); i++) { - mExtendedSlots->mCustomElementData->mCallbackQueue[i]->Traverse(cb); + i < mExtendedSlots->mCustomElementData->mReactionQueue.Length(); i++) { + if (mExtendedSlots->mCustomElementData->mReactionQueue[i]) { + mExtendedSlots->mCustomElementData->mReactionQueue[i]->Traverse(cb); + } } } diff --git a/dom/tests/mochitest/webcomponents/test_document_register_stack.html b/dom/tests/mochitest/webcomponents/test_document_register_stack.html index 34f108654..b1c61af11 100644 --- a/dom/tests/mochitest/webcomponents/test_document_register_stack.html +++ b/dom/tests/mochitest/webcomponents/test_document_register_stack.html @@ -28,7 +28,8 @@ function testChangeAttributeInCreatedCallback() { createdCallbackCalled = true; is(attributeChangedCallbackCalled, false, "Attribute changed callback should not have been called prior to setting the attribute."); this.setAttribute("foo", "bar"); - is(attributeChangedCallbackCalled, false, "While element is being created, element should not be added to the current element callback queue."); + is(attributeChangedCallbackCalled, true, "While element is being created, element should be added to the current element callback queue."); + runNextTest(); }; p.attributeChangedCallback = function(name, oldValue, newValue) { @@ -36,7 +37,6 @@ function testChangeAttributeInCreatedCallback() { is(attributeChangedCallbackCalled, false, "attributeChanged callback should only be called once in this tests."); is(newValue, "bar", "The new value should be 'bar'"); attributeChangedCallbackCalled = true; - runNextTest(); }; document.registerElement("x-one", { prototype: p }); |