Bug 1359346 - Implement custom element state;

https://dom.spec.whatwg.org/#concept-element-custom-element-state

Tag UXP Issue #1344
This commit is contained in:
Gaming4JC 2020-01-04 13:24:58 -05:00 • committed by Roy Tam
commit cb89330bf3
3 changed files with 44 additions and 22 deletions

View file

@ -78,12 +78,21 @@ CustomElementCallback::CustomElementCallback(Element* aThisObject,
{ {
} }
//-----------------------------------------------------
// CustomElementData
CustomElementData::CustomElementData(nsIAtom* aType) CustomElementData::CustomElementData(nsIAtom* aType)
: mType(aType), : CustomElementData(aType, CustomElementData::State::eUndefined)
mCurrentCallback(-1), {
mElementIsBeingCreated(false), }
mCreatedCallbackInvoked(true),
mAssociatedMicroTask(-1) CustomElementData::CustomElementData(nsIAtom* aType, State aState)
: mType(aType)
, mCurrentCallback(-1)
, mElementIsBeingCreated(false)
, mCreatedCallbackInvoked(true)
, mAssociatedMicroTask(-1)
, mState(aState)
{ {
} }
@ -99,6 +108,9 @@ CustomElementData::RunCallbackQueue()
mCurrentCallback = -1; mCurrentCallback = -1;
} }
//-----------------------------------------------------
// CustomElementRegistry
// Only needed for refcounted objects. // Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry) NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
@ -305,10 +317,15 @@ CustomElementRegistry::SetupCustomElement(Element* aElement,
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true); aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
} }
CustomElementDefinition* data = LookupCustomElementDefinition( // SetupCustomElement() should be called with an element that don't have
// CustomElementData setup, if not we will hit the assertion in
// SetCustomElementData().
aElement->SetCustomElementData(new CustomElementData(typeAtom));
CustomElementDefinition* definition = LookupCustomElementDefinition(
aElement->NodeInfo()->LocalName(), aTypeExtension); aElement->NodeInfo()->LocalName(), aTypeExtension);
if (!data) { if (!definition) {
// The type extension doesn't exist in the registry, // The type extension doesn't exist in the registry,
// thus we don't need to enqueue callback or adjust // thus we don't need to enqueue callback or adjust
// the "is" attribute, but it is possibly an upgrade candidate. // the "is" attribute, but it is possibly an upgrade candidate.
@ -316,7 +333,7 @@ CustomElementRegistry::SetupCustomElement(Element* aElement,
return; return;
} }
if (data->mLocalName != tagAtom) { if (definition->mLocalName != tagAtom) {
// The element doesn't match the local name for the // The element doesn't match the local name for the
// definition, thus the element isn't a custom element // definition, thus the element isn't a custom element
// and we don't need to do anything more. // and we don't need to do anything more.
@ -325,7 +342,7 @@ CustomElementRegistry::SetupCustomElement(Element* aElement,
// Enqueuing the created callback will set the CustomElementData on the // Enqueuing the created callback will set the CustomElementData on the
// element, causing prototype swizzling to occur in Element::WrapObject. // element, causing prototype swizzling to occur in Element::WrapObject.
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data); EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, definition);
} }
void void
@ -334,7 +351,8 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType
LifecycleCallbackArgs* aArgs, LifecycleCallbackArgs* aArgs,
CustomElementDefinition* aDefinition) CustomElementDefinition* aDefinition)
{ {
CustomElementData* elementData = aCustomElement->GetCustomElementData(); RefPtr<CustomElementData> elementData = aCustomElement->GetCustomElementData();
MOZ_ASSERT(elementData, "CustomElementData should exist");
// Let DEFINITION be ELEMENT's definition // Let DEFINITION be ELEMENT's definition
CustomElementDefinition* definition = aDefinition; CustomElementDefinition* definition = aDefinition;
@ -354,16 +372,6 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType
} }
} }
if (!elementData) {
// Create the custom element data the first time
// that we try to enqueue a callback.
elementData = new CustomElementData(definition->mType);
// aCustomElement takes ownership of elementData
aCustomElement->SetCustomElementData(elementData);
MOZ_ASSERT(aType == nsIDocument::eCreated,
"First callback should be the created callback");
}
// Let CALLBACK be the callback associated with the key NAME in CALLBACKS. // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
CallbackFunction* func = nullptr; CallbackFunction* func = nullptr;
switch (aType) { switch (aType) {
@ -881,8 +889,6 @@ CustomElementRegistry::Upgrade(Element* aElement,
} }
} // Leave prototype's compartment. } // Leave prototype's compartment.
// Enqueuing the created callback will set the CustomElementData on the
// element, causing prototype swizzling to occur in Element::WrapObject.
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, aDefinition); EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, aDefinition);
} }

View file

@ -71,7 +71,18 @@ struct CustomElementData
{ {
NS_INLINE_DECL_REFCOUNTING(CustomElementData) NS_INLINE_DECL_REFCOUNTING(CustomElementData)
// https://dom.spec.whatwg.org/#concept-element-custom-element-state
// CustomElementData is only created on the element which is a custom element
// or an upgrade candidate, so the state of an element without
// CustomElementData is "uncustomized".
enum class State {
eUndefined,
eFailed,
eCustom
};
explicit CustomElementData(nsIAtom* aType); explicit CustomElementData(nsIAtom* aType);
CustomElementData(nsIAtom* aType, State aState);
// Objects in this array are transient and empty after each microtask // Objects in this array are transient and empty after each microtask
// checkpoint. // checkpoint.
nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue; nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue;
@ -89,6 +100,8 @@ struct CustomElementData
// it is used to determine if a new queue needs to be pushed onto the // it is used to determine if a new queue needs to be pushed onto the
// processing stack. // processing stack.
int32_t mAssociatedMicroTask; int32_t mAssociatedMicroTask;
// Custom element state as described in the custom element spec.
State mState;
// Empties the callback queue. // Empties the callback queue.
void RunCallbackQueue(); void RunCallbackQueue();

View file

@ -3550,6 +3550,9 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER); element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
} }
element->SetCustomElementData(
new CustomElementData(definition->mType, CustomElementData::State::eCustom));
return element.forget(); return element.forget();
} }