diff options
Diffstat (limited to 'dom/base/CustomElementRegistry.cpp')
-rw-r--r-- | dom/base/CustomElementRegistry.cpp | 186 |
1 files changed, 157 insertions, 29 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index 6cd213210..20eb59e94 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -137,6 +137,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry) cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value()); } } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -237,6 +238,7 @@ CustomElementRegistry::sProcessingStack; CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) , mIsCustomDefinitionRunning(false) + , mIsBackupQueueProcessing(false) { mozilla::HoldJSObjects(this); @@ -502,35 +504,7 @@ CustomElementRegistry::UpgradeCandidates(JSContext* aCx, continue; } - elem->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 (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); + EnqueueUpgradeReaction(elem, aDefinition); } } } @@ -590,6 +564,9 @@ CustomElementRegistry::Define(const nsAString& aName, const ElementDefinitionOptions& aOptions, 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(); AutoJSAPI jsapi; @@ -883,6 +860,148 @@ CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv) 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, nsIAtom* aLocalName, JSObject* aConstructor, @@ -898,5 +1017,14 @@ CustomElementDefinition::CustomElementDefinition(nsIAtom* aType, { } +//----------------------------------------------------- +// CustomElementUpgradeReaction + +/* virtual */ void +CustomElementUpgradeReaction::Invoke(Element* aElement) +{ + mRegistry->Upgrade(aElement, mDefinition); +} + } // namespace dom } // namespace mozilla |