/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
struct CustomElementData; struct ElementDefinitionOptions; class CallbackFunction; class CustomElementCallback; class CustomElementReaction; class DocGroup; class Promise;
// Each custom element has an associated callback queue and an element is // being created flag. struct CustomElementData { // https://dom.spec.whatwg.org/#concept-element-custom-element-state // 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". enumclass State { eUndefined, eFailed, eCustom, ePrecustomized };
explicit CustomElementData(nsAtom* aType);
CustomElementData(nsAtom* aType, State aState);
~CustomElementData() = default;
// Custom element state as described in the custom element spec.
State mState; // custom element reaction queue as described in the custom element spec. // There is 1 reaction in reaction queue, when 1) it becomes disconnected, // 2) it’s adopted into a new document, 3) its attributes are changed, // appended, removed, or replaced. // There are 3 reactions in reaction queue when doing upgrade operation, // e.g., create an element, insert a node.
AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue;
nsAtom* GetIs(const Element* aElement) const { // If mType isn't the same as name atom, this is a customized built-in // element, which has 'is' value set. return aElement->NodeInfo()->NameAtom() == mType ? nullptr : mType.get();
}
private: // Custom element type, for <button is="x-button"> or <x-button> // this would be x-button.
RefPtr<nsAtom> mType;
RefPtr<CustomElementDefinition> mCustomElementDefinition;
RefPtr<ElementInternals> mElementInternals; bool mIsAttachedInternals = false;
};
#define ALREADY_CONSTRUCTED_MARKER nullptr
// The required information for a custom element as defined in: // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition struct CustomElementDefinition {
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(CustomElementDefinition)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
// The type (name) for this custom element, for <button is="x-foo"> or <x-foo> // this would be x-foo.
RefPtr<nsAtom> mType;
// The localname to (e.g. <button is=type> -- this would be button).
RefPtr<nsAtom> mLocalName;
// The namespace for this custom element
int32_t mNamespaceID;
// The custom element constructor.
RefPtr<CustomElementConstructor> mConstructor;
// The list of attributes that this custom element observes.
nsTArray<RefPtr<nsAtom>> mObservedAttributes;
// The lifecycle callbacks to call for this custom element.
UniquePtr<LifecycleCallbacks> mCallbacks;
UniquePtr<FormAssociatedLifecycleCallbacks> mFormAssociatedCallbacks;
// If this is true, user agent treats elements associated to this custom // element definition as form-associated custom elements. bool mFormAssociated = false;
// Determine whether to allow to attachInternals() for this custom element. bool mDisableInternals = false;
// Determine whether to allow to attachShadow() for this custom element. bool mDisableShadow = false;
// A construction stack. Use nullptr to represent an "already constructed // marker".
nsTArray<RefPtr<Element>> mConstructionStack;
// See step 6.1.10 of https://dom.spec.whatwg.org/#concept-create-element // which set up the prefix after a custom element is created. However, In // Gecko, the prefix isn't allowed to be changed in NodeInfo, so we store the // prefix information here and propagate to where NodeInfo is assigned to a // custom element instead.
nsTArray<RefPtr<nsAtom>> mPrefixStack;
// This basically is used for distinguishing the custom element constructor // is invoked from document.createElement or directly from JS, i.e. // `new CustomElementConstructor()`.
uint32_t mConstructionDepth = 0;
// Hold a strong reference of Element so that it does not get cycle collected // before the reactions in its reaction queue are invoked. // The element reaction queues are stored in CustomElementData. // We need to lookup ElementReactionQueueMap again to get relevant reaction // queue. The choice of 3 for the auto size here is based on running Custom // Elements wpt tests. typedef AutoTArray<RefPtr<Element>, 3> ElementQueue;
/** * [CEReactions] Before executing the algorithm's steps. * Increase the current recursion depth, and the element queue is pushed * lazily when we really enqueue reactions. * * @return true if the element queue is pushed for "previous" recursion depth.
*/ bool EnterCEReactions() { bool temp = mIsElementQueuePushedForCurrentRecursionDepth;
mRecursionDepth++; // The is-element-queue-pushed flag is initially false when entering a new // recursion level. The original value will be cached in AutoCEReaction // and restored after leaving this recursion level.
mIsElementQueuePushedForCurrentRecursionDepth = false; return temp;
}
/** * [CEReactions] After executing the algorithm's steps. * Pop and invoke the element queue if it is created and pushed for current * recursion depth, then decrease the current recursion depth. * * @param aCx JSContext used for handling exception thrown by algorithm's * steps, this could be a nullptr. * aWasElementQueuePushed used for restoring status after leaving * current recursion.
*/
MOZ_CAN_RUN_SCRIPT void LeaveCEReactions(JSContext* aCx, bool aWasElementQueuePushed) {
MOZ_ASSERT(mRecursionDepth);
if (mIsElementQueuePushedForCurrentRecursionDepth) {
Maybe<JS::AutoSaveExceptionState> ases; if (aCx) {
ases.emplace(aCx);
}
PopAndInvokeElementQueue();
}
mRecursionDepth--; // Restore the is-element-queue-pushed flag cached in AutoCEReaction when // leaving the recursion level.
mIsElementQueuePushedForCurrentRecursionDepth = aWasElementQueuePushed;
/** * Push a new element queue onto the custom element reactions stack.
*/ void CreateAndPushElementQueue();
/** * Pop the element queue from the custom element reactions stack, and invoke * custom element reactions in that queue.
*/
MOZ_CAN_RUN_SCRIPT void PopAndInvokeElementQueue();
// Current [CEReactions] recursion depth.
uint32_t mRecursionDepth; // True if the element queue is pushed into reaction stack for current // recursion depth. This will be cached in AutoCEReaction when entering a new // CEReaction recursion and restored after leaving the recursion. bool mIsElementQueuePushedForCurrentRecursionDepth;
private: class BackupQueueMicroTask final : public mozilla::MicroTaskRunnable { public: explicit BackupQueueMicroTask(CustomElementReactionsStack* aReactionStack)
: MicroTaskRunnable(), mReactionStack(aReactionStack) {
MOZ_ASSERT(!mReactionStack->mIsBackupQueueProcessing, "mIsBackupQueueProcessing should be initially false");
mReactionStack->mIsBackupQueueProcessing = true;
}
class CustomElementRegistry final : public nsISupports, public nsWrapperCache { public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
private: class RunCustomElementCreationCallback : public mozilla::Runnable { public: // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. // See bug 1535398.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
NS_DECL_NSIRUNNABLE
/** * To allow native code to call methods of chrome-implemented custom elements, * a helper method may be defined in the custom element called * 'getCustomInterfaceCallback'. This method takes an IID and returns an * object which implements an XPCOM interface. * * This returns null if aElement is not from a chrome document.
*/ static already_AddRefed<nsISupports> CallGetCustomInterface(
Element* aElement, const nsIID& aIID);
/** * Registers an unresolved custom element that is a candidate for * upgrade. |aTypeName| is the name of the custom element type, if it is not * provided, then element name is used. |aTypeName| should be provided * when registering a custom element that extends an existing * element. e.g. <button is="x-button">.
*/ void RegisterUnresolvedElement(Element* aElement,
nsAtom* aTypeName = nullptr);
/** * Unregister an unresolved custom element that is a candidate for * upgrade when a custom element is removed from tree.
*/ void UnregisterUnresolvedElement(Element* aElement,
nsAtom* aTypeName = nullptr);
/** * Register an element to be upgraded when the custom element creation * callback is executed. * * To be used when LookupCustomElementDefinition() didn't return a definition, * but with the callback scheduled to be run.
*/ inlinevoid RegisterCallbackUpgradeElement(Element* aElement,
nsAtom* aTypeName = nullptr) { if (mElementCreationCallbacksUpgradeCandidatesMap.IsEmpty()) { return;
}
using DefinitionMap =
nsRefPtrHashtable<nsAtomHashKey, CustomElementDefinition>; using ElementCreationCallbackMap =
nsRefPtrHashtable<nsAtomHashKey, CustomElementCreationCallback>; using CandidateMap =
nsClassHashtable<nsAtomHashKey, nsTHashSet<RefPtr<nsIWeakReference>>>; using ConstructorMap =
JS::GCHashMap<JS::Heap<JSObject*>, RefPtr<nsAtom>,
js::StableCellHasher<JS::Heap<JSObject*>>,
js::SystemAllocPolicy>;
// Hashtable for custom element definitions in web components. // Custom prototypes are stored in the compartment where definition was // defined.
DefinitionMap mCustomDefinitions;
// Hashtable for chrome-only callbacks that is called *before* we return // a CustomElementDefinition, when the typeAtom matches. // The callbacks are registered with the setElementCreationCallback method.
ElementCreationCallbackMap mElementCreationCallbacks;
// Hashtable for looking up definitions by using constructor as key. // Custom elements' name are stored here and we need to lookup // mCustomDefinitions again to get definitions.
ConstructorMap mConstructors;
using WhenDefinedPromiseMap = nsRefPtrHashtable<nsAtomHashKey, Promise>;
WhenDefinedPromiseMap mWhenDefinedPromiseMap;
// The "upgrade candidates map" from the web components spec. Maps from a // namespace id and local name to a list of elements to upgrade if that // element is registered as a custom element.
CandidateMap mCandidatesMap;
// If an element creation callback is found, the nsTHashtable for the // type is created here, and elements will later be upgraded.
CandidateMap mElementCreationCallbacksUpgradeCandidatesMap;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
// It is used to prevent reentrant invocations of element definition. bool mIsCustomDefinitionRunning;
// Chrome-only method that give JS an opportunity to only load the custom // element definition script when needed. void SetElementCreationCallback(const nsAString& aName,
CustomElementCreationCallback& aCallback,
ErrorResult& aRv);
void Upgrade(nsINode& aRoot);
};
class MOZ_RAII AutoCEReaction final { public: // JSContext is allowed to be a nullptr if we are guaranteeing that we're // not doing something that might throw but not finish reporting a JS // exception during the lifetime of the AutoCEReaction.
AutoCEReaction(CustomElementReactionsStack* aReactionsStack, JSContext* aCx)
: mReactionsStack(aReactionsStack), mCx(aCx) {
mIsElementQueuePushedForPreviousRecursionDepth =
mReactionsStack->EnterCEReactions();
}
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because this is called from Maybe<>.reset().
MOZ_CAN_RUN_SCRIPT_BOUNDARY ~AutoCEReaction() {
mReactionsStack->LeaveCEReactions(
mCx, mIsElementQueuePushedForPreviousRecursionDepth);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.