diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 674d7ef99cb3a..f8cc443b88bfb 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -1143,6 +1143,10 @@ static void DoUpgrade(Element* aElement, CustomElementDefinition* aDefinition, return; } + RefPtr data = aElement->GetCustomElementData(); + MOZ_ASSERT(data, "CustomElementData should exist"); + data->mState = CustomElementData::State::ePrecustomized; + JS::Rooted constructResult(RootingCx()); // Rethrow the exception since it might actually throw the exception from the // upgrade steps back out to the caller of document.createElement. @@ -1223,7 +1227,13 @@ void CustomElementRegistry::Upgrade(Element* aElement, DoUpgrade(aElement, aDefinition, MOZ_KnownLive(aDefinition->mConstructor), aRv); if (aRv.Failed()) { - MOZ_ASSERT(data->mState == CustomElementData::State::eFailed); + MOZ_ASSERT(data->mState == CustomElementData::State::eFailed || + data->mState == CustomElementData::State::ePrecustomized); + // Spec doesn't set custom element state to failed here, but without this we + // would have inconsistent state on a custom elemet that is failed to + // upgrade, see https://github.com/whatwg/html/issues/6929, and + // https://github.com/web-platform-tests/wpt/pull/29911 for the test. + data->mState = CustomElementData::State::eFailed; aElement->SetCustomElementDefinition(nullptr); // Empty element's custom element reaction queue. data->mReactionQueue.Clear(); diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h index 16d3c3dfd3dd9..87d74ebcb68b7 100644 --- a/dom/base/CustomElementRegistry.h +++ b/dom/base/CustomElementRegistry.h @@ -97,7 +97,7 @@ struct CustomElementData { // 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 }; + enum class State { eUndefined, eFailed, eCustom, ePrecustomized }; explicit CustomElementData(nsAtom* aType); CustomElementData(nsAtom* aType, State aState); diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 37e864ea31221..58affa4e3c273 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2882,7 +2882,7 @@ void nsGenericHTMLElement::SetInnerText(const nsAString& aValue) { mb.NodesAdded(); } -// https://html.spec.whatwg.org/commit-snapshots/b48bb2238269d90ea4f455a52cdf29505aff3df0/#dom-attachinternals +// https://html.spec.whatwg.org/commit-snapshots/53bc3803433e1c817918b83e8a84f3db900031dd/#dom-attachinternals already_AddRefed nsGenericHTMLElement::AttachInternals( ErrorResult& aRv) { CustomElementData* ceData = GetCustomElementData(); @@ -2932,6 +2932,10 @@ already_AddRefed nsGenericHTMLElement::AttachInternals( return nullptr; } + // If this is not a custom element, i.e. ceData is nullptr, we are unable to + // find a definition and should return earlier above. + MOZ_ASSERT(ceData); + // 5. If element's attached internals is true, then throw an // "NotSupportedError" DOMException. if (ceData->HasAttachedInternals()) { @@ -2941,10 +2945,19 @@ already_AddRefed nsGenericHTMLElement::AttachInternals( return nullptr; } - // 6. Set element's attached internals to true. + // 6. If element's custom element state is not "precustomized" or "custom", + // then throw a "NotSupportedError" DOMException. + if (ceData->mState != CustomElementData::State::ePrecustomized && + ceData->mState != CustomElementData::State::eCustom) { + aRv.ThrowNotSupportedError( + R"(Custom element state is not "precustomized" or "custom".)"); + return nullptr; + } + + // 7. Set element's attached internals to true. ceData->AttachedInternals(); - // 7. Create a new ElementInternals instance targeting element, and return it. + // 8. Create a new ElementInternals instance targeting element, and return it. return MakeAndAddRef(this); }