summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2020-01-04 23:29:10 -0500
committerGaming4JC <g4jc@hyperbola.info>2020-01-26 15:50:22 -0500
commit53c9b77ed41aebb157012eff5e57cad3a962d18e (patch)
tree774cec41db2f86d1621497b7df20899bc9255622
parente6733c9278d0e9687be83de5b5f409a43653fbee (diff)
downloadUXP-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
-rw-r--r--dom/base/CustomElementRegistry.cpp160
-rw-r--r--dom/base/CustomElementRegistry.h67
-rw-r--r--dom/base/FragmentOrElement.cpp6
-rw-r--r--dom/tests/mochitest/webcomponents/test_document_register_stack.html4
-rw-r--r--layout/build/nsLayoutStatics.cpp2
5 files changed, 101 insertions, 138 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 });
diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp
index 0f4560afe..27236e3f4 100644
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -412,8 +412,6 @@ nsLayoutStatics::Shutdown()
DisplayItemClip::Shutdown();
- CustomElementRegistry::XPCOMShutdown();
-
CacheObserver::Shutdown();
PromiseDebugging::Shutdown();