Bug 1299363 - Part 5-2: Implement new upgrade steps.

Tag UXP Issue #1344
This commit is contained in:
Gaming4JC 2020-01-05 11:21:03 -05:00 committed by Roy Tam
commit 729ffa53f3
2 changed files with 177 additions and 35 deletions

View file

@ -19,7 +19,7 @@ namespace dom {
void
CustomElementCallback::Call()
{
ErrorResult rv;
IgnoredErrorResult rv;
switch (mType) {
case nsIDocument::eCreated:
{
@ -55,10 +55,6 @@ CustomElementCallback::Call()
mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
break;
}
// If callbacks throw exceptions, it'll be handled and reported in
// Lifecycle*Callback::Call function.
rv.SuppressException();
}
void
@ -82,6 +78,38 @@ CustomElementCallback::CustomElementCallback(Element* aThisObject,
{
}
//-----------------------------------------------------
// CustomElementConstructor
already_AddRefed<Element>
CustomElementConstructor::Construct(const char* aExecutionReason,
ErrorResult& aRv)
{
CallSetup s(this, aRv, aExecutionReason,
CallbackFunction::eRethrowExceptions);
JSContext* cx = s.GetContext();
if (!cx) {
MOZ_ASSERT(aRv.Failed());
return nullptr;
}
JS::Rooted<JSObject*> result(cx);
JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback));
if (!JS::Construct(cx, constructor, JS::HandleValueArray::empty(), &result)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
RefPtr<Element> element;
if (NS_FAILED(UNWRAP_OBJECT(Element, &result, element))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
return element.forget();
}
//-----------------------------------------------------
// CustomElementData
@ -101,6 +129,33 @@ CustomElementData::CustomElementData(nsIAtom* aType, State aState)
//-----------------------------------------------------
// CustomElementRegistry
namespace {
class MOZ_RAII AutoConstructionStackEntry final
{
public:
AutoConstructionStackEntry(nsTArray<RefPtr<nsGenericHTMLElement>>& aStack,
nsGenericHTMLElement* aElement)
: mStack(aStack)
{
mIndex = mStack.Length();
mStack.AppendElement(aElement);
}
~AutoConstructionStackEntry()
{
MOZ_ASSERT(mIndex == mStack.Length() - 1,
"Removed element should be the last element");
mStack.RemoveElementAt(mIndex);
}
private:
nsTArray<RefPtr<nsGenericHTMLElement>>& mStack;
uint32_t mIndex;
};
} // namespace anonymous
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
@ -139,6 +194,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
"mCustomDefinitions->mCallbacks->mDetachedCallback");
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
}
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCustomDefinitions->mConstructor");
cb.NoteXPCOMChild(iter.UserData()->mConstructor);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
@ -147,9 +206,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry)
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
aCallbacks.Trace(&iter.UserData()->mConstructor,
"mCustomDefinitions constructor",
aClosure);
aCallbacks.Trace(&iter.UserData()->mPrototype,
"mCustomDefinitions prototype",
aClosure);
@ -418,6 +474,7 @@ CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey,
return;
}
// TODO: Bug 1326028 - Upgrade custom element in shadow-including tree order
nsAutoPtr<nsTArray<nsWeakPtr>> candidates;
mCandidatesMap.RemoveAndForget(aKey, candidates);
if (candidates) {
@ -696,7 +753,7 @@ CustomElementRegistry::Define(const nsAString& aName,
CustomElementDefinition* definition =
new CustomElementDefinition(nameAtom,
localNameAtom,
constructor,
&aFunctionConstructor,
constructorPrototype,
callbacks,
0 /* TODO dependent on HTML imports. Bug 877072 */);
@ -717,7 +774,6 @@ CustomElementRegistry::Define(const nsAString& aName,
/**
* 13. 14. 15. Upgrade candidates
*/
// TODO: Bug 1299363 - Implement custom element v1 upgrade algorithm
UpgradeCandidates(nameAtom, definition, aRv);
/**
@ -748,7 +804,7 @@ CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
return;
}
aRetVal.setObject(*data->mConstructor);
aRetVal.setObjectOrNull(data->mConstructor->Callable());
return;
}
@ -782,23 +838,67 @@ CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
return promise.forget();
}
void
CustomElementRegistry::Upgrade(Element* aElement,
CustomElementDefinition* aDefinition)
{
// TODO: This function will be replaced to v1 upgrade in bug 1299363
aElement->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
namespace {
// Make sure that the element name matches the name in the definition.
// (e.g. a definition for x-button extending button should match
// <button is="x-button"> but not <x-button>.
if (aElement->NodeInfo()->NameAtom() != aDefinition->mLocalName) {
//Skip over this element because definition does not apply.
static void
DoUpgrade(Element* aElement,
CustomElementConstructor* aConstructor,
ErrorResult& aRv)
{
// Rethrow the exception since it might actually throw the exception from the
// upgrade steps back out to the caller of document.createElement.
RefPtr<Element> constructResult =
aConstructor->Construct("Custom Element Upgrade", aRv);
if (aRv.Failed()) {
return;
}
MOZ_ASSERT(aElement->IsHTMLElement(aDefinition->mLocalName));
if (constructResult.get() != aElement) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
}
} // anonymous namespace
// https://html.spec.whatwg.org/multipage/scripting.html#upgrades
void
CustomElementRegistry::Upgrade(Element* aElement,
CustomElementDefinition* aDefinition,
ErrorResult& aRv)
{
aElement->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
RefPtr<CustomElementData> data = aElement->GetCustomElementData();
MOZ_ASSERT(data, "CustomElementData should exist");
// Step 1 and step 2.
if (data->mState == CustomElementData::State::eCustom ||
data->mState == CustomElementData::State::eFailed) {
return;
}
// Step 3 and Step 4.
// TODO: Bug 1334051 - Implement list of observed attributes for custom elements' attributeChanged callbacks
// TODO: Bug 1334043 - Implement connected lifecycle callbacks for custom elements
// Step 5.
AutoConstructionStackEntry acs(aDefinition->mConstructionStack,
nsGenericHTMLElement::FromContent(aElement));
// Step 6 and step 7.
DoUpgrade(aElement, aDefinition->mConstructor, aRv);
if (aRv.Failed()) {
data->mState = CustomElementData::State::eFailed;
// Empty element's custom element reaction queue.
data->mReactionQueue.Clear();
return;
}
// Step 8.
data->mState = CustomElementData::State::eCustom;
// This is for old spec.
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, aDefinition);
}
@ -824,7 +924,13 @@ CustomElementReactionsStack::PopAndInvokeElementQueue()
ElementQueue* elementQueue = mReactionsStack.ElementAt(lastIndex).get();
// Check element queue size in order to reduce function call overhead.
if (!elementQueue->IsEmpty()) {
InvokeReactions(elementQueue);
// It is still not clear what error reporting will look like in custom
// element, see https://github.com/w3c/webcomponents/issues/635.
// We usually report the error to entry global in gecko, so just follow the
// same behavior here.
nsIGlobalObject* global = GetEntryGlobal();
MOZ_ASSERT(global, "Should always have a entry global here!");
InvokeReactions(elementQueue, global);
}
// InvokeReactions() might create other custom element reactions, but those
@ -887,13 +993,24 @@ CustomElementReactionsStack::InvokeBackupQueue()
{
// Check backup queue size in order to reduce function call overhead.
if (!mBackupQueue.IsEmpty()) {
InvokeReactions(&mBackupQueue);
// Upgrade reactions won't be scheduled in backup queue and the exception of
// callback reactions will be automatically reported in CallSetup.
// If the reactions are invoked from backup queue (in microtask check point),
// we don't need to pass global object for error reporting.
InvokeReactions(&mBackupQueue, nullptr);
}
}
void
CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue)
CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue,
nsIGlobalObject* aGlobal)
{
// This is used for error reporting.
Maybe<AutoEntryScript> aes;
if (aGlobal) {
aes.emplace(aGlobal, "custom elements reaction invocation");
}
// Note: It's possible to re-enter this method.
for (uint32_t i = 0; i < aElementQueue->Length(); ++i) {
nsCOMPtr<Element> element = do_QueryReferent(aElementQueue->ElementAt(i));
@ -911,7 +1028,16 @@ CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue)
// this funciton. The entry will be removed when bug 1379573 is landed.
auto reaction(Move(reactions.ElementAt(j)));
if (reaction) {
reaction->Invoke(element);
ErrorResult rv;
reaction->Invoke(element, rv);
if (aes) {
JSContext* cx = aes->cx();
if (rv.MaybeSetPendingException(cx)) {
aes->ReportException();
}
MOZ_ASSERT(!JS_IsExceptionPending(cx));
}
MOZ_ASSERT(!rv.Failed());
}
}
reactions.Clear();
@ -924,13 +1050,13 @@ CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue)
CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName,
JSObject* aConstructor,
Function* aConstructor,
JSObject* aPrototype,
LifecycleCallbacks* aCallbacks,
uint32_t aDocOrder)
: mType(aType),
mLocalName(aLocalName),
mConstructor(aConstructor),
mConstructor(new CustomElementConstructor(aConstructor)),
mPrototype(aPrototype),
mCallbacks(aCallbacks),
mDocOrder(aDocOrder)
@ -941,16 +1067,16 @@ CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
// CustomElementUpgradeReaction
/* virtual */ void
CustomElementUpgradeReaction::Invoke(Element* aElement)
CustomElementUpgradeReaction::Invoke(Element* aElement, ErrorResult& aRv)
{
mRegistry->Upgrade(aElement, mDefinition);
mRegistry->Upgrade(aElement, mDefinition, aRv);
}
//-----------------------------------------------------
// CustomElementCallbackReaction
/* virtual */ void
CustomElementCallbackReaction::Invoke(Element* aElement)
CustomElementCallbackReaction::Invoke(Element* aElement, ErrorResult& aRv)
{
mCustomElementCallback->Call();
}