summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2020-01-05 11:21:03 -0500
committerGaming4JC <g4jc@hyperbola.info>2020-01-26 15:50:25 -0500
commit76006f6f33bc0550302212ee32e41d569f927c37 (patch)
treeb82489a50d50b80be46593871c8011dc98475914
parentca6296d1e75123d8c4763bb877c25e4532a2f3af (diff)
downloaduxp-76006f6f33bc0550302212ee32e41d569f927c37.tar.gz
Bug 1299363 - Part 5-2: Implement new upgrade steps.
Tag UXP Issue mcp-graveyard/UXP#1344
-rw-r--r--dom/base/CustomElementRegistry.cpp182
-rw-r--r--dom/base/CustomElementRegistry.h30
2 files changed, 177 insertions, 35 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
index ce2e4b04fe..e619ad599f 100644
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -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
@@ -83,6 +79,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
CustomElementData::CustomElementData(nsIAtom* aType)
@@ -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();
}
+namespace {
+
+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;
+ }
+
+ 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)
+ CustomElementDefinition* aDefinition,
+ ErrorResult& aRv)
{
- // TODO: This function will be replaced to v1 upgrade in bug 1299363
aElement->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
- // 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.
+ 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;
}
- MOZ_ASSERT(aElement->IsHTMLElement(aDefinition->mLocalName));
+ // 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();
}
diff --git a/dom/base/CustomElementRegistry.h b/dom/base/CustomElementRegistry.h
index 620492cbb7..c45f3f70c4 100644
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -66,6 +66,18 @@ private:
CustomElementData* mOwnerData;
};
+class CustomElementConstructor final : public CallbackFunction
+{
+public:
+ explicit CustomElementConstructor(CallbackFunction* aOther)
+ : CallbackFunction(aOther)
+ {
+ MOZ_ASSERT(JS::IsConstructor(mCallback));
+ }
+
+ already_AddRefed<Element> Construct(const char* aExecutionReason, ErrorResult& aRv);
+};
+
// Each custom element has an associated callback queue and an element is
// being created flag.
struct CustomElementData
@@ -114,7 +126,7 @@ struct CustomElementDefinition
{
CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName,
- JSObject* aConstructor,
+ Function* aConstructor,
JSObject* aPrototype,
mozilla::dom::LifecycleCallbacks* aCallbacks,
uint32_t aDocOrder);
@@ -126,7 +138,7 @@ struct CustomElementDefinition
nsCOMPtr<nsIAtom> mLocalName;
// The custom element constructor.
- JS::Heap<JSObject *> mConstructor;
+ RefPtr<CustomElementConstructor> mConstructor;
// The prototype to use for new custom elements of this type.
JS::Heap<JSObject *> mPrototype;
@@ -156,7 +168,7 @@ public:
}
virtual ~CustomElementReaction() = default;
- virtual void Invoke(Element* aElement) = 0;
+ virtual void Invoke(Element* aElement, ErrorResult& aRv) = 0;
virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const
{
}
@@ -176,7 +188,7 @@ public:
}
private:
- virtual void Invoke(Element* aElement) override;
+ virtual void Invoke(Element* aElement, ErrorResult& aRv) override;
};
class CustomElementCallbackReaction final : public CustomElementReaction
@@ -196,7 +208,7 @@ class CustomElementCallbackReaction final : public CustomElementReaction
}
private:
- virtual void Invoke(Element* aElement) override;
+ virtual void Invoke(Element* aElement, ErrorResult& aRv) override;
UniquePtr<CustomElementCallback> mCustomElementCallback;
};
@@ -258,7 +270,7 @@ private:
* Invoke custom element reactions
* https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions
*/
- void InvokeReactions(ElementQueue* aElementQueue);
+ void InvokeReactions(ElementQueue* aElementQueue, nsIGlobalObject* aGlobal);
void Enqueue(Element* aElement, CustomElementReaction* aReaction);
@@ -326,7 +338,11 @@ public:
void GetCustomPrototype(nsIAtom* aAtom,
JS::MutableHandle<JSObject*> aPrototype);
- void Upgrade(Element* aElement, CustomElementDefinition* aDefinition);
+ /**
+ * Upgrade an element.
+ * https://html.spec.whatwg.org/multipage/scripting.html#upgrades
+ */
+ void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
private:
~CustomElementRegistry();