зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1309184
- Implement upgrade reaction for custom element reactions. r=wchen
--HG-- extra : rebase_source : 0333c91029b6e08961e2ad0e7c04c3364cb429b5
This commit is contained in:
Родитель
254a3c92b7
Коммит
4335473f7c
|
@ -137,6 +137,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
|
||||||
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
|
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
@ -238,6 +239,7 @@ CustomElementRegistry::sProcessingStack;
|
||||||
CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow)
|
CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow)
|
||||||
: mWindow(aWindow)
|
: mWindow(aWindow)
|
||||||
, mIsCustomDefinitionRunning(false)
|
, mIsCustomDefinitionRunning(false)
|
||||||
|
, mIsBackupQueueProcessing(false)
|
||||||
{
|
{
|
||||||
mozilla::HoldJSObjects(this);
|
mozilla::HoldJSObjects(this);
|
||||||
|
|
||||||
|
@ -503,35 +505,7 @@ CustomElementRegistry::UpgradeCandidates(JSContext* aCx,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
elem->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
|
EnqueueUpgradeReaction(elem, aDefinition);
|
||||||
|
|
||||||
// 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 (elem->NodeInfo()->NameAtom() != aDefinition->mLocalName) {
|
|
||||||
//Skip over this element because definition does not apply.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(elem->IsHTMLElement(aDefinition->mLocalName));
|
|
||||||
nsWrapperCache* cache;
|
|
||||||
CallQueryInterface(elem, &cache);
|
|
||||||
MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
|
|
||||||
|
|
||||||
// We want to set the custom prototype in the caller's comparment.
|
|
||||||
// In the case that element is in a different compartment,
|
|
||||||
// this will set the prototype on the element's wrapper and
|
|
||||||
// thus only visible in the wrapper's compartment.
|
|
||||||
JS::RootedObject wrapper(aCx);
|
|
||||||
JS::Rooted<JSObject*> prototype(aCx, aDefinition->mPrototype);
|
|
||||||
if ((wrapper = cache->GetWrapper()) && JS_WrapObject(aCx, &wrapper)) {
|
|
||||||
if (!JS_SetPrototype(aCx, wrapper, prototype)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsContentUtils::EnqueueLifecycleCallback(
|
|
||||||
elem->OwnerDoc(), nsIDocument::eCreated, elem, nullptr, aDefinition);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -591,6 +565,9 @@ CustomElementRegistry::Define(const nsAString& aName,
|
||||||
const ElementDefinitionOptions& aOptions,
|
const ElementDefinitionOptions& aOptions,
|
||||||
ErrorResult& aRv)
|
ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
|
// We do this for [CEReaction] temporarily and it will be removed
|
||||||
|
// after webidl supports [CEReaction] annotation in bug 1309147.
|
||||||
|
AutoCEReaction ceReaction(this);
|
||||||
aRv.MightThrowJSException();
|
aRv.MightThrowJSException();
|
||||||
|
|
||||||
AutoJSAPI jsapi;
|
AutoJSAPI jsapi;
|
||||||
|
@ -884,6 +861,147 @@ CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
|
||||||
return promise.forget();
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CustomElementRegistry::EnqueueUpgradeReaction(Element* aElement,
|
||||||
|
CustomElementDefinition* aDefinition)
|
||||||
|
{
|
||||||
|
Enqueue(aElement, new CustomElementUpgradeReaction(this, aDefinition));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(aElement->IsHTMLElement(aDefinition->mLocalName));
|
||||||
|
nsWrapperCache* cache;
|
||||||
|
CallQueryInterface(aElement, &cache);
|
||||||
|
MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
|
||||||
|
|
||||||
|
AutoJSAPI jsapi;
|
||||||
|
if (NS_WARN_IF(!jsapi.Init(mWindow))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSContext *cx = jsapi.cx();
|
||||||
|
// We want to set the custom prototype in the the compartment of define()'s caller.
|
||||||
|
// We store the prototype from define() directly,
|
||||||
|
// hence the prototype's compartment is the caller's compartment.
|
||||||
|
JS::RootedObject wrapper(cx);
|
||||||
|
JS::Rooted<JSObject*> prototype(cx, aDefinition->mPrototype);
|
||||||
|
{ // Enter prototype's compartment.
|
||||||
|
JSAutoCompartment ac(cx, prototype);
|
||||||
|
|
||||||
|
if ((wrapper = cache->GetWrapper()) && JS_WrapObject(cx, &wrapper)) {
|
||||||
|
if (!JS_SetPrototype(cx, wrapper, prototype)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
CustomElementRegistry::CreateAndPushElementQueue()
|
||||||
|
{
|
||||||
|
// Push a new element queue onto the custom element reactions stack.
|
||||||
|
mReactionsStack.AppendElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CustomElementRegistry::PopAndInvokeElementQueue()
|
||||||
|
{
|
||||||
|
// Pop the element queue from the custom element reactions stack,
|
||||||
|
// and invoke custom element reactions in that queue.
|
||||||
|
MOZ_ASSERT(!mReactionsStack.IsEmpty(),
|
||||||
|
"Reaction stack shouldn't be empty");
|
||||||
|
|
||||||
|
ElementQueue& elementQueue = mReactionsStack.LastElement();
|
||||||
|
CustomElementRegistry::InvokeReactions(elementQueue);
|
||||||
|
DebugOnly<bool> isRemovedElement = mReactionsStack.RemoveElement(elementQueue);
|
||||||
|
MOZ_ASSERT(isRemovedElement,
|
||||||
|
"Reaction stack should have an element queue to remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CustomElementRegistry::Enqueue(Element* aElement,
|
||||||
|
CustomElementReaction* aReaction)
|
||||||
|
{
|
||||||
|
// Add element to the current element queue.
|
||||||
|
if (!mReactionsStack.IsEmpty()) {
|
||||||
|
mReactionsStack.LastElement().AppendElement(do_GetWeakReference(aElement));
|
||||||
|
ReactionQueue* reactionQueue =
|
||||||
|
mElementReactionQueueMap.LookupOrAdd(aElement);
|
||||||
|
reactionQueue->AppendElement(aReaction);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the custom element reactions stack is empty, then:
|
||||||
|
// Add element to the backup element queue.
|
||||||
|
mBackupQueue.AppendElement(do_GetWeakReference(aElement));
|
||||||
|
|
||||||
|
ReactionQueue* reactionQueue =
|
||||||
|
mElementReactionQueueMap.LookupOrAdd(aElement);
|
||||||
|
reactionQueue->AppendElement(aReaction);
|
||||||
|
|
||||||
|
if (mIsBackupQueueProcessing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
|
||||||
|
RefPtr<ProcessBackupQueueRunnable> processBackupQueueRunnable =
|
||||||
|
new ProcessBackupQueueRunnable(this);
|
||||||
|
context->DispatchToMicroTask(processBackupQueueRunnable.forget());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CustomElementRegistry::InvokeBackupQueue()
|
||||||
|
{
|
||||||
|
CustomElementRegistry::InvokeReactions(mBackupQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CustomElementRegistry::InvokeReactions(ElementQueue& aElementQueue)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < aElementQueue.Length(); ++i) {
|
||||||
|
nsCOMPtr<Element> element = do_QueryReferent(aElementQueue[i]);
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoPtr<ReactionQueue> reactions;
|
||||||
|
mElementReactionQueueMap.RemoveAndForget(element, reactions);
|
||||||
|
|
||||||
|
MOZ_ASSERT(reactions,
|
||||||
|
"Associated ReactionQueue must be found in mElementReactionQueueMap");
|
||||||
|
|
||||||
|
for (uint32_t j = 0; j < reactions->Length(); ++j) {
|
||||||
|
nsAutoPtr<CustomElementReaction>& reaction = reactions->ElementAt(j);
|
||||||
|
reaction->Invoke(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aElementQueue.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------
|
||||||
|
// CustomElementDefinition
|
||||||
|
|
||||||
CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
|
CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
|
||||||
nsIAtom* aLocalName,
|
nsIAtom* aLocalName,
|
||||||
JSObject* aConstructor,
|
JSObject* aConstructor,
|
||||||
|
@ -899,5 +1017,15 @@ CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------
|
||||||
|
// CustomElementUpgradeReaction
|
||||||
|
|
||||||
|
/* virtual */ void
|
||||||
|
CustomElementUpgradeReaction::Invoke(Element* aElement)
|
||||||
|
{
|
||||||
|
mRegistry->Upgrade(aElement, mDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -134,6 +134,37 @@ struct CustomElementDefinition
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CustomElementReaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CustomElementReaction(CustomElementRegistry* aRegistry,
|
||||||
|
CustomElementDefinition* aDefinition)
|
||||||
|
: mRegistry(aRegistry)
|
||||||
|
, mDefinition(aDefinition)
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~CustomElementReaction() = default;
|
||||||
|
virtual void Invoke(Element* aElement) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CustomElementRegistry* mRegistry;
|
||||||
|
CustomElementDefinition* mDefinition;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CustomElementUpgradeReaction final : public CustomElementReaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CustomElementUpgradeReaction(CustomElementRegistry* aRegistry,
|
||||||
|
CustomElementDefinition* aDefinition)
|
||||||
|
: CustomElementReaction(aRegistry, aDefinition)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void Invoke(Element* aElement) override;
|
||||||
|
};
|
||||||
|
|
||||||
class CustomElementRegistry final : public nsISupports,
|
class CustomElementRegistry final : public nsISupports,
|
||||||
public nsWrapperCache
|
public nsWrapperCache
|
||||||
{
|
{
|
||||||
|
@ -177,6 +208,24 @@ public:
|
||||||
void GetCustomPrototype(nsIAtom* aAtom,
|
void GetCustomPrototype(nsIAtom* aAtom,
|
||||||
JS::MutableHandle<JSObject*> aPrototype);
|
JS::MutableHandle<JSObject*> aPrototype);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueue a custom element upgrade reaction
|
||||||
|
* https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-upgrade-reaction
|
||||||
|
*/
|
||||||
|
void EnqueueUpgradeReaction(Element* aElement,
|
||||||
|
CustomElementDefinition* aDefinition);
|
||||||
|
|
||||||
|
void Upgrade(Element* aElement, CustomElementDefinition* aDefinition);
|
||||||
|
|
||||||
|
// [CEReactions] Before executing the algorithm's steps
|
||||||
|
// Push a new element queue onto the custom element reactions stack.
|
||||||
|
void CreateAndPushElementQueue();
|
||||||
|
|
||||||
|
// [CEReactions] After executing the algorithm's steps
|
||||||
|
// Pop the element queue from the custom element reactions stack,
|
||||||
|
// and invoke custom element reactions in that queue.
|
||||||
|
void PopAndInvokeElementQueue();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
|
explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
|
||||||
~CustomElementRegistry();
|
~CustomElementRegistry();
|
||||||
|
@ -198,6 +247,21 @@ private:
|
||||||
nsIAtom* aKey,
|
nsIAtom* aKey,
|
||||||
CustomElementDefinition* aDefinition);
|
CustomElementDefinition* aDefinition);
|
||||||
|
|
||||||
|
void InvokeBackupQueue();
|
||||||
|
|
||||||
|
void Enqueue(Element* aElement, CustomElementReaction* aReaction);
|
||||||
|
|
||||||
|
// nsWeakPtr is a weak pointer of Element
|
||||||
|
// The element reaction queues are stored in ElementReactionQueueMap.
|
||||||
|
// We need to lookup ElementReactionQueueMap again to get relevant reaction queue.
|
||||||
|
typedef nsTArray<nsWeakPtr> ElementQueue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke custom element reactions
|
||||||
|
* https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions
|
||||||
|
*/
|
||||||
|
void InvokeReactions(ElementQueue& aElementQueue);
|
||||||
|
|
||||||
typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition>
|
typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition>
|
||||||
DefinitionMap;
|
DefinitionMap;
|
||||||
typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
|
typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
|
||||||
|
@ -238,6 +302,17 @@ private:
|
||||||
|
|
||||||
// It is used to prevent reentrant invocations of element definition.
|
// It is used to prevent reentrant invocations of element definition.
|
||||||
bool mIsCustomDefinitionRunning;
|
bool mIsCustomDefinitionRunning;
|
||||||
|
// https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue
|
||||||
|
bool mIsBackupQueueProcessing;
|
||||||
|
|
||||||
|
typedef nsTArray<nsAutoPtr<CustomElementReaction>> ReactionQueue;
|
||||||
|
typedef nsClassHashtable<nsISupportsHashKey, ReactionQueue>
|
||||||
|
ElementReactionQueueMap;
|
||||||
|
|
||||||
|
ElementReactionQueueMap mElementReactionQueueMap;
|
||||||
|
|
||||||
|
nsTArray<ElementQueue> mReactionsStack;
|
||||||
|
ElementQueue mBackupQueue;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class MOZ_RAII AutoSetRunningFlag final {
|
class MOZ_RAII AutoSetRunningFlag final {
|
||||||
|
@ -258,6 +333,28 @@ private:
|
||||||
CustomElementRegistry* mRegistry;
|
CustomElementRegistry* mRegistry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
class ProcessBackupQueueRunnable : public mozilla::Runnable {
|
||||||
|
public:
|
||||||
|
explicit ProcessBackupQueueRunnable(CustomElementRegistry* aRegistry)
|
||||||
|
: mRegistry(aRegistry)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(!mRegistry->mIsBackupQueueProcessing,
|
||||||
|
"mIsBackupQueueProcessing should be initially false");
|
||||||
|
mRegistry->mIsBackupQueueProcessing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD Run() override
|
||||||
|
{
|
||||||
|
mRegistry->InvokeBackupQueue();
|
||||||
|
mRegistry->mIsBackupQueueProcessing = false;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<CustomElementRegistry> mRegistry;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
nsISupports* GetParentObject() const;
|
nsISupports* GetParentObject() const;
|
||||||
|
|
||||||
|
@ -272,6 +369,19 @@ public:
|
||||||
already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv);
|
already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MOZ_RAII AutoCEReaction final {
|
||||||
|
public:
|
||||||
|
explicit AutoCEReaction(CustomElementRegistry* aRegistry)
|
||||||
|
: mRegistry(aRegistry) {
|
||||||
|
mRegistry->CreateAndPushElementQueue();
|
||||||
|
}
|
||||||
|
~AutoCEReaction() {
|
||||||
|
mRegistry->PopAndInvokeElementQueue();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
RefPtr<CustomElementRegistry> mRegistry;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче