2017-10-27 01:08:41 +03:00
|
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2016-07-25 00:38:00 +03:00
|
|
|
|
/* 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/. */
|
|
|
|
|
|
2016-10-12 10:26:02 +03:00
|
|
|
|
#ifndef mozilla_dom_CustomElementRegistry_h
|
|
|
|
|
#define mozilla_dom_CustomElementRegistry_h
|
2016-07-25 00:38:00 +03:00
|
|
|
|
|
2016-09-07 12:55:21 +03:00
|
|
|
|
#include "js/GCHashTable.h"
|
2016-07-25 00:38:00 +03:00
|
|
|
|
#include "js/TypeDecls.h"
|
|
|
|
|
#include "mozilla/Attributes.h"
|
2018-05-10 08:04:12 +03:00
|
|
|
|
#include "mozilla/CycleCollectedJSContext.h" // for MicroTaskRunnable
|
2016-07-25 00:38:00 +03:00
|
|
|
|
#include "mozilla/ErrorResult.h"
|
|
|
|
|
#include "mozilla/dom/BindingDeclarations.h"
|
2018-05-11 21:23:31 +03:00
|
|
|
|
#include "mozilla/dom/CustomElementRegistryBinding.h"
|
2017-03-28 12:05:12 +03:00
|
|
|
|
#include "mozilla/dom/Element.h"
|
2017-09-25 09:46:00 +03:00
|
|
|
|
#include "mozilla/dom/WebComponentsBinding.h"
|
2016-07-25 00:38:00 +03:00
|
|
|
|
#include "nsCycleCollectionParticipant.h"
|
2017-09-25 09:46:00 +03:00
|
|
|
|
#include "nsGenericHTMLElement.h"
|
2016-07-25 00:38:00 +03:00
|
|
|
|
#include "nsWrapperCache.h"
|
2017-08-23 09:36:00 +03:00
|
|
|
|
#include "nsContentUtils.h"
|
2016-07-25 00:38:00 +03:00
|
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
namespace dom {
|
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
struct CustomElementData;
|
2016-07-25 00:38:00 +03:00
|
|
|
|
struct ElementDefinitionOptions;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
class CallbackFunction;
|
2017-03-21 12:02:21 +03:00
|
|
|
|
class CustomElementReaction;
|
2017-11-27 11:10:27 +03:00
|
|
|
|
class DocGroup;
|
2016-07-25 00:38:00 +03:00
|
|
|
|
class Promise;
|
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
struct LifecycleCallbackArgs {
|
|
|
|
|
nsString name;
|
|
|
|
|
nsString oldValue;
|
|
|
|
|
nsString newValue;
|
2017-08-28 06:25:00 +03:00
|
|
|
|
nsString namespaceURI;
|
2018-08-27 22:20:53 +03:00
|
|
|
|
|
|
|
|
|
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
};
|
|
|
|
|
|
2017-10-02 07:42:00 +03:00
|
|
|
|
struct LifecycleAdoptedCallbackArgs {
|
2019-01-02 16:05:23 +03:00
|
|
|
|
RefPtr<Document> mOldDocument;
|
|
|
|
|
RefPtr<Document> mNewDocument;
|
2017-10-02 07:42:00 +03:00
|
|
|
|
};
|
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
class CustomElementCallback {
|
|
|
|
|
public:
|
|
|
|
|
CustomElementCallback(Element* aThisObject,
|
2019-01-02 16:05:23 +03:00
|
|
|
|
Document::ElementCallbackType aCallbackType,
|
2017-10-13 18:36:54 +03:00
|
|
|
|
CallbackFunction* aCallback);
|
2016-08-30 06:48:53 +03:00
|
|
|
|
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
|
2018-08-27 22:20:53 +03:00
|
|
|
|
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
void Call();
|
|
|
|
|
void SetArgs(LifecycleCallbackArgs& aArgs) {
|
2019-01-02 16:05:23 +03:00
|
|
|
|
MOZ_ASSERT(mType == Document::eAttributeChanged,
|
2016-08-30 06:48:53 +03:00
|
|
|
|
"Arguments are only used by attribute changed callback.");
|
|
|
|
|
mArgs = aArgs;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-02 07:42:00 +03:00
|
|
|
|
void SetAdoptedCallbackArgs(
|
|
|
|
|
LifecycleAdoptedCallbackArgs& aAdoptedCallbackArgs) {
|
2019-01-02 16:05:23 +03:00
|
|
|
|
MOZ_ASSERT(mType == Document::eAdopted,
|
2017-10-02 07:42:00 +03:00
|
|
|
|
"Arguments are only used by adopted callback.");
|
|
|
|
|
mAdoptedCallbackArgs = aAdoptedCallbackArgs;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
private:
|
|
|
|
|
// The this value to use for invocation of the callback.
|
|
|
|
|
RefPtr<Element> mThisObject;
|
|
|
|
|
RefPtr<CallbackFunction> mCallback;
|
|
|
|
|
// The type of callback (eCreated, eAttached, etc.)
|
2019-01-02 16:05:23 +03:00
|
|
|
|
Document::ElementCallbackType mType;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
// Arguments to be passed to the callback,
|
|
|
|
|
// used by the attribute changed callback.
|
|
|
|
|
LifecycleCallbackArgs mArgs;
|
2017-10-02 07:42:00 +03:00
|
|
|
|
LifecycleAdoptedCallbackArgs mAdoptedCallbackArgs;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Each custom element has an associated callback queue and an element is
|
|
|
|
|
// being created flag.
|
|
|
|
|
struct CustomElementData {
|
|
|
|
|
NS_INLINE_DECL_REFCOUNTING(CustomElementData)
|
|
|
|
|
|
2016-12-28 06:43:29 +03:00
|
|
|
|
// 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".
|
|
|
|
|
enum class State { eUndefined, eFailed, eCustom };
|
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
|
explicit CustomElementData(nsAtom* aType);
|
|
|
|
|
CustomElementData(nsAtom* aType, State aState);
|
2017-11-14 14:23:00 +03:00
|
|
|
|
|
2016-12-28 06:43:29 +03:00
|
|
|
|
// Custom element state as described in the custom element spec.
|
|
|
|
|
State mState;
|
2017-03-21 12:02:21 +03:00
|
|
|
|
// 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.
|
2017-07-19 03:30:00 +03:00
|
|
|
|
AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
|
2017-10-13 06:54:46 +03:00
|
|
|
|
void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
|
|
|
|
|
CustomElementDefinition* GetCustomElementDefinition();
|
2018-09-22 00:29:25 +03:00
|
|
|
|
nsAtom* GetCustomElementType() const { return mType; }
|
2017-09-25 09:46:00 +03:00
|
|
|
|
|
2017-10-13 06:54:46 +03:00
|
|
|
|
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
|
|
|
|
|
void Unlink();
|
2018-08-27 22:20:53 +03:00
|
|
|
|
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
|
2017-09-25 09:46:00 +03:00
|
|
|
|
|
2018-09-22 00:29:25 +03:00
|
|
|
|
nsAtom* GetIs(const Element* aElement) const {
|
2018-06-28 14:22:58 +03:00
|
|
|
|
// 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();
|
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
private:
|
|
|
|
|
virtual ~CustomElementData() {}
|
2017-10-13 06:54:46 +03:00
|
|
|
|
|
2017-11-14 14:23:00 +03:00
|
|
|
|
// Custom element type, for <button is="x-button"> or <x-button>
|
|
|
|
|
// this would be x-button.
|
|
|
|
|
RefPtr<nsAtom> mType;
|
2017-10-13 06:54:46 +03:00
|
|
|
|
RefPtr<CustomElementDefinition> mCustomElementDefinition;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
};
|
|
|
|
|
|
2018-03-27 22:49:02 +03:00
|
|
|
|
#define ALREADY_CONSTRUCTED_MARKER nullptr
|
2016-12-22 06:47:19 +03:00
|
|
|
|
|
2016-07-22 00:42:00 +03:00
|
|
|
|
// The required information for a custom element as defined in:
|
2016-09-01 10:11:32 +03:00
|
|
|
|
// https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
|
2016-07-22 00:42:00 +03:00
|
|
|
|
struct CustomElementDefinition {
|
2017-09-25 09:46:00 +03:00
|
|
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
|
|
|
|
|
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
|
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
|
CustomElementDefinition(nsAtom* aType, nsAtom* aLocalName,
|
2019-04-10 18:45:11 +03:00
|
|
|
|
int32_t aNamespaceID,
|
|
|
|
|
CustomElementConstructor* aConstructor,
|
2017-10-03 01:05:19 +03:00
|
|
|
|
nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
|
2018-05-18 23:18:19 +03:00
|
|
|
|
UniquePtr<LifecycleCallbacks>&& aCallbacks);
|
2016-07-22 00:42:00 +03:00
|
|
|
|
|
2017-10-13 06:54:46 +03:00
|
|
|
|
// The type (name) for this custom element, for <button is="x-foo"> or <x-foo>
|
|
|
|
|
// this would be x-foo.
|
2017-10-03 01:05:19 +03:00
|
|
|
|
RefPtr<nsAtom> mType;
|
2016-07-22 00:42:00 +03:00
|
|
|
|
|
|
|
|
|
// The localname to (e.g. <button is=type> -- this would be button).
|
2017-10-03 01:05:19 +03:00
|
|
|
|
RefPtr<nsAtom> mLocalName;
|
2016-07-22 00:42:00 +03:00
|
|
|
|
|
2018-08-15 13:31:16 +03:00
|
|
|
|
// The namespace for this custom element
|
|
|
|
|
int32_t mNamespaceID;
|
|
|
|
|
|
2016-09-01 10:11:32 +03:00
|
|
|
|
// The custom element constructor.
|
2017-08-13 11:40:37 +03:00
|
|
|
|
RefPtr<CustomElementConstructor> mConstructor;
|
2016-09-01 10:11:32 +03:00
|
|
|
|
|
2017-08-28 06:25:00 +03:00
|
|
|
|
// The list of attributes that this custom element observes.
|
2017-10-03 01:05:19 +03:00
|
|
|
|
nsTArray<RefPtr<nsAtom>> mObservedAttributes;
|
2017-08-28 06:25:00 +03:00
|
|
|
|
|
2016-07-22 00:42:00 +03:00
|
|
|
|
// The lifecycle callbacks to call for this custom element.
|
2018-05-18 23:18:19 +03:00
|
|
|
|
UniquePtr<LifecycleCallbacks> mCallbacks;
|
2016-07-22 00:42:00 +03:00
|
|
|
|
|
2016-12-22 06:47:19 +03:00
|
|
|
|
// A construction stack. Use nullptr to represent an "already constructed
|
|
|
|
|
// marker".
|
2017-10-11 01:25:10 +03:00
|
|
|
|
nsTArray<RefPtr<Element>> mConstructionStack;
|
2016-07-22 00:42:00 +03:00
|
|
|
|
|
2018-09-04 01:13:35 +03:00
|
|
|
|
// 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;
|
|
|
|
|
|
2016-11-17 18:31:50 +03:00
|
|
|
|
bool IsCustomBuiltIn() { return mType != mLocalName; }
|
2017-08-28 06:25:00 +03:00
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
|
bool IsInObservedAttributeList(nsAtom* aName) {
|
2017-08-28 06:25:00 +03:00
|
|
|
|
if (mObservedAttributes.IsEmpty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mObservedAttributes.Contains(aName);
|
|
|
|
|
}
|
2017-09-25 09:46:00 +03:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
~CustomElementDefinition() {}
|
2016-07-22 00:42:00 +03:00
|
|
|
|
};
|
|
|
|
|
|
2017-01-06 02:38:00 +03:00
|
|
|
|
class CustomElementReaction {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~CustomElementReaction() = default;
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2017-08-13 11:40:37 +03:00
|
|
|
|
virtual void Invoke(Element* aElement, ErrorResult& aRv) = 0;
|
2018-05-30 13:24:44 +03:00
|
|
|
|
virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const = 0;
|
2018-08-27 22:20:53 +03:00
|
|
|
|
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const = 0;
|
2017-10-17 12:55:08 +03:00
|
|
|
|
|
|
|
|
|
bool IsUpgradeReaction() { return mIsUpgradeReaction; }
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
bool mIsUpgradeReaction = false;
|
2017-01-06 02:38:00 +03:00
|
|
|
|
};
|
|
|
|
|
|
2017-03-21 07:42:00 +03:00
|
|
|
|
// https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack
|
|
|
|
|
class CustomElementReactionsStack {
|
|
|
|
|
public:
|
|
|
|
|
NS_INLINE_DECL_REFCOUNTING(CustomElementReactionsStack)
|
|
|
|
|
|
|
|
|
|
CustomElementReactionsStack()
|
|
|
|
|
: mIsBackupQueueProcessing(false),
|
2017-11-28 11:58:50 +03:00
|
|
|
|
mRecursionDepth(0),
|
|
|
|
|
mIsElementQueuePushedForCurrentRecursionDepth(false) {}
|
2017-03-21 07:42:00 +03:00
|
|
|
|
|
2017-09-26 08:56:11 +03:00
|
|
|
|
// Hold a strong reference of Element so that it does not get cycle collected
|
|
|
|
|
// before the reactions in its reaction queue are invoked.
|
2017-03-21 12:02:21 +03:00
|
|
|
|
// The element reaction queues are stored in CustomElementData.
|
2017-03-21 07:42:00 +03:00
|
|
|
|
// We need to lookup ElementReactionQueueMap again to get relevant reaction
|
2018-05-25 21:19:18 +03:00
|
|
|
|
// queue. The choice of 3 for the auto size here is based on running Custom
|
|
|
|
|
// Elements wpt tests.
|
|
|
|
|
typedef AutoTArray<RefPtr<Element>, 3> ElementQueue;
|
2017-03-21 07:42:00 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Enqueue a custom element upgrade reaction
|
|
|
|
|
* https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-upgrade-reaction
|
|
|
|
|
*/
|
2017-09-26 08:56:00 +03:00
|
|
|
|
void EnqueueUpgradeReaction(Element* aElement,
|
2017-03-21 07:42:00 +03:00
|
|
|
|
CustomElementDefinition* aDefinition);
|
|
|
|
|
|
2017-07-19 03:30:00 +03:00
|
|
|
|
/**
|
|
|
|
|
* Enqueue a custom element callback reaction
|
|
|
|
|
* https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-callback-reaction
|
|
|
|
|
*/
|
2017-09-26 08:56:00 +03:00
|
|
|
|
void EnqueueCallbackReaction(
|
|
|
|
|
Element* aElement,
|
2017-07-19 03:30:00 +03:00
|
|
|
|
UniquePtr<CustomElementCallback> aCustomElementCallback);
|
|
|
|
|
|
2017-11-28 11:58:50 +03:00
|
|
|
|
/**
|
|
|
|
|
* [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;
|
|
|
|
|
}
|
2017-03-21 07:42:00 +03:00
|
|
|
|
|
2017-11-28 11:58:50 +03:00
|
|
|
|
/**
|
|
|
|
|
* [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.
|
|
|
|
|
*/
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2017-11-28 11:58:50 +03:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
MOZ_ASSERT_IF(!mRecursionDepth, mReactionsStack.IsEmpty());
|
|
|
|
|
}
|
2017-03-21 07:42:00 +03:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
~CustomElementReactionsStack(){};
|
|
|
|
|
|
2017-11-28 11:58:50 +03:00
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT void PopAndInvokeElementQueue();
|
2017-11-28 11:58:50 +03:00
|
|
|
|
|
2017-03-30 12:35:59 +03:00
|
|
|
|
// The choice of 8 for the auto size here is based on gut feeling.
|
2017-01-09 17:12:57 +03:00
|
|
|
|
AutoTArray<UniquePtr<ElementQueue>, 8> mReactionsStack;
|
2017-03-21 07:42:00 +03:00
|
|
|
|
ElementQueue mBackupQueue;
|
|
|
|
|
// https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue
|
|
|
|
|
bool mIsBackupQueueProcessing;
|
|
|
|
|
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT void InvokeBackupQueue();
|
2017-03-21 07:42:00 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invoke custom element reactions
|
|
|
|
|
* https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions
|
|
|
|
|
*/
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2017-08-13 11:40:37 +03:00
|
|
|
|
void InvokeReactions(ElementQueue* aElementQueue, nsIGlobalObject* aGlobal);
|
2017-03-21 07:42:00 +03:00
|
|
|
|
|
|
|
|
|
void Enqueue(Element* aElement, CustomElementReaction* aReaction);
|
|
|
|
|
|
2017-11-28 11:58:50 +03:00
|
|
|
|
// 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;
|
|
|
|
|
|
2017-03-21 07:42:00 +03:00
|
|
|
|
private:
|
2017-11-03 12:38:46 +03:00
|
|
|
|
class BackupQueueMicroTask final : public mozilla::MicroTaskRunnable {
|
2017-03-21 07:42:00 +03:00
|
|
|
|
public:
|
2017-06-12 22:34:10 +03:00
|
|
|
|
explicit BackupQueueMicroTask(CustomElementReactionsStack* aReactionStack)
|
|
|
|
|
: MicroTaskRunnable(), mReactionStack(aReactionStack) {
|
2017-03-21 07:42:00 +03:00
|
|
|
|
MOZ_ASSERT(!mReactionStack->mIsBackupQueueProcessing,
|
|
|
|
|
"mIsBackupQueueProcessing should be initially false");
|
|
|
|
|
mReactionStack->mIsBackupQueueProcessing = true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT virtual void Run(AutoSlowOperation& aAso) override {
|
2017-03-21 07:42:00 +03:00
|
|
|
|
mReactionStack->InvokeBackupQueue();
|
|
|
|
|
mReactionStack->mIsBackupQueueProcessing = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2019-04-09 16:37:23 +03:00
|
|
|
|
const RefPtr<CustomElementReactionsStack> mReactionStack;
|
2017-03-21 07:42:00 +03:00
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2016-10-12 10:26:02 +03:00
|
|
|
|
class CustomElementRegistry final : public nsISupports, public nsWrapperCache {
|
2016-07-25 00:38:00 +03:00
|
|
|
|
public:
|
|
|
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
2016-10-12 10:26:02 +03:00
|
|
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
|
2016-07-25 00:38:00 +03:00
|
|
|
|
|
|
|
|
|
public:
|
2017-02-23 13:24:05 +03:00
|
|
|
|
explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
|
|
|
|
|
|
2018-05-11 21:23:31 +03:00
|
|
|
|
private:
|
|
|
|
|
class RunCustomElementCreationCallback : public mozilla::Runnable {
|
|
|
|
|
public:
|
2019-04-10 15:51:15 +03:00
|
|
|
|
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.
|
|
|
|
|
// See bug 1535398.
|
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
2018-05-11 21:23:31 +03:00
|
|
|
|
NS_DECL_NSIRUNNABLE
|
2019-04-10 15:51:15 +03:00
|
|
|
|
|
2018-05-11 21:23:31 +03:00
|
|
|
|
explicit RunCustomElementCreationCallback(
|
|
|
|
|
CustomElementRegistry* aRegistry, nsAtom* aAtom,
|
|
|
|
|
CustomElementCreationCallback* aCallback)
|
|
|
|
|
: mozilla::Runnable(
|
|
|
|
|
"CustomElementRegistry::RunCustomElementCreationCallback"),
|
|
|
|
|
mRegistry(aRegistry),
|
|
|
|
|
mAtom(aAtom),
|
|
|
|
|
mCallback(aCallback) {}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
|
2018-05-11 21:23:31 +03:00
|
|
|
|
private:
|
|
|
|
|
RefPtr<CustomElementRegistry> mRegistry;
|
|
|
|
|
RefPtr<nsAtom> mAtom;
|
|
|
|
|
RefPtr<CustomElementCreationCallback> mCallback;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public:
|
2018-09-22 00:29:25 +03:00
|
|
|
|
/**
|
|
|
|
|
* Returns whether there's a definition that is likely to match this type
|
|
|
|
|
* atom. This is not exact, so should only be used for optimization, but it's
|
|
|
|
|
* good enough to prove that the chrome code doesn't need an XBL binding.
|
|
|
|
|
*/
|
|
|
|
|
bool IsLikelyToBeCustomElement(nsAtom* aTypeAtom) const {
|
|
|
|
|
return mCustomDefinitions.GetWeak(aTypeAtom) ||
|
|
|
|
|
mElementCreationCallbacks.GetWeak(aTypeAtom);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
/**
|
|
|
|
|
* Looking up a custom element definition.
|
|
|
|
|
* https://html.spec.whatwg.org/#look-up-a-custom-element-definition
|
|
|
|
|
*/
|
|
|
|
|
CustomElementDefinition* LookupCustomElementDefinition(nsAtom* aNameAtom,
|
2018-08-15 13:31:16 +03:00
|
|
|
|
int32_t aNameSpaceID,
|
|
|
|
|
nsAtom* aTypeAtom);
|
2016-08-30 06:48:53 +03:00
|
|
|
|
|
2016-11-17 18:31:50 +03:00
|
|
|
|
CustomElementDefinition* LookupCustomElementDefinition(
|
|
|
|
|
JSContext* aCx, JSObject* aConstructor) const;
|
|
|
|
|
|
2017-09-26 08:56:00 +03:00
|
|
|
|
static void EnqueueLifecycleCallback(
|
2019-01-02 16:05:23 +03:00
|
|
|
|
Document::ElementCallbackType aType, Element* aCustomElement,
|
2017-09-26 08:56:00 +03:00
|
|
|
|
LifecycleCallbackArgs* aArgs,
|
2017-10-02 07:42:00 +03:00
|
|
|
|
LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
|
2017-09-26 08:56:00 +03:00
|
|
|
|
CustomElementDefinition* aDefinition);
|
2016-08-30 06:48:53 +03:00
|
|
|
|
|
2017-08-13 11:40:37 +03:00
|
|
|
|
/**
|
|
|
|
|
* Upgrade an element.
|
|
|
|
|
* https://html.spec.whatwg.org/multipage/scripting.html#upgrades
|
|
|
|
|
*/
|
2019-04-10 15:51:15 +03:00
|
|
|
|
MOZ_CAN_RUN_SCRIPT
|
2017-09-25 08:33:00 +03:00
|
|
|
|
static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition,
|
|
|
|
|
ErrorResult& aRv);
|
2017-01-06 02:38:00 +03:00
|
|
|
|
|
2018-07-19 15:38:45 +03:00
|
|
|
|
/**
|
|
|
|
|
* 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
|
2018-09-19 13:46:41 +03:00
|
|
|
|
* object which implements an XPCOM interface.
|
2018-07-19 15:38:45 +03:00
|
|
|
|
*
|
|
|
|
|
* This returns null if aElement is not from a chrome document.
|
|
|
|
|
*/
|
|
|
|
|
static already_AddRefed<nsISupports> CallGetCustomInterface(
|
|
|
|
|
Element* aElement, const nsIID& aIID);
|
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
/**
|
|
|
|
|
* Registers an unresolved custom element that is a candidate for
|
2017-11-16 07:23:15 +03:00
|
|
|
|
* upgrade. |aTypeName| is the name of the custom element type, if it is not
|
2016-08-30 06:48:53 +03:00
|
|
|
|
* 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,
|
2017-10-03 01:05:19 +03:00
|
|
|
|
nsAtom* aTypeName = nullptr);
|
2016-08-30 06:48:53 +03:00
|
|
|
|
|
2017-11-14 14:25:00 +03:00
|
|
|
|
/**
|
|
|
|
|
* 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);
|
2018-05-23 08:04:18 +03:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
inline void RegisterCallbackUpgradeElement(Element* aElement,
|
|
|
|
|
nsAtom* aTypeName = nullptr) {
|
|
|
|
|
if (mElementCreationCallbacksUpgradeCandidatesMap.IsEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RefPtr<nsAtom> typeName = aTypeName;
|
|
|
|
|
if (!typeName) {
|
|
|
|
|
typeName = aElement->NodeInfo()->NameAtom();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>* elements =
|
|
|
|
|
mElementCreationCallbacksUpgradeCandidatesMap.Get(typeName);
|
|
|
|
|
|
|
|
|
|
// If there isn't a table, there won't be a definition added by the
|
|
|
|
|
// callback.
|
|
|
|
|
if (!elements) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsWeakPtr elem = do_GetWeakReference(aElement);
|
|
|
|
|
elements->PutEntry(elem);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-14 14:25:00 +03:00
|
|
|
|
private:
|
|
|
|
|
~CustomElementRegistry();
|
|
|
|
|
|
|
|
|
|
static UniquePtr<CustomElementCallback> CreateCustomElementCallback(
|
2019-01-02 16:05:23 +03:00
|
|
|
|
Document::ElementCallbackType aType, Element* aCustomElement,
|
2017-11-14 14:25:00 +03:00
|
|
|
|
LifecycleCallbackArgs* aArgs,
|
|
|
|
|
LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
|
|
|
|
|
CustomElementDefinition* aDefinition);
|
|
|
|
|
|
2017-03-21 07:42:00 +03:00
|
|
|
|
void UpgradeCandidates(nsAtom* aKey, CustomElementDefinition* aDefinition,
|
|
|
|
|
ErrorResult& aRv);
|
2017-01-06 02:38:00 +03:00
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
|
typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>, CustomElementDefinition>
|
2016-08-30 06:48:53 +03:00
|
|
|
|
DefinitionMap;
|
2018-05-11 21:23:31 +03:00
|
|
|
|
typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>,
|
|
|
|
|
CustomElementCreationCallback>
|
|
|
|
|
ElementCreationCallbackMap;
|
2018-04-07 12:50:01 +03:00
|
|
|
|
typedef nsClassHashtable<nsRefPtrHashKey<nsAtom>,
|
|
|
|
|
nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>>
|
2016-08-30 06:48:53 +03:00
|
|
|
|
CandidateMap;
|
2016-09-07 12:55:21 +03:00
|
|
|
|
typedef JS::GCHashMap<JS::Heap<JSObject*>, RefPtr<nsAtom>,
|
|
|
|
|
js::MovableCellHasher<JS::Heap<JSObject*>>,
|
|
|
|
|
js::SystemAllocPolicy>
|
|
|
|
|
ConstructorMap;
|
2016-08-30 06:48:53 +03:00
|
|
|
|
|
|
|
|
|
// Hashtable for custom element definitions in web components.
|
2017-11-16 07:23:15 +03:00
|
|
|
|
// Custom prototypes are stored in the compartment where definition was
|
|
|
|
|
// defined.
|
2016-08-30 06:48:53 +03:00
|
|
|
|
DefinitionMap mCustomDefinitions;
|
|
|
|
|
|
2018-05-11 21:23:31 +03:00
|
|
|
|
// 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;
|
|
|
|
|
|
2016-09-07 12:55:21 +03:00
|
|
|
|
// 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;
|
|
|
|
|
|
2017-10-03 01:05:19 +03:00
|
|
|
|
typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>, Promise>
|
2016-10-07 09:16:00 +03:00
|
|
|
|
WhenDefinedPromiseMap;
|
|
|
|
|
WhenDefinedPromiseMap mWhenDefinedPromiseMap;
|
2016-09-07 12:55:21 +03:00
|
|
|
|
|
2016-08-30 06:48:53 +03:00
|
|
|
|
// 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;
|
|
|
|
|
|
2018-05-23 08:04:18 +03:00
|
|
|
|
// If an element creation callback is found, the nsTHashtable for the
|
|
|
|
|
// type is created here, and elements will later be upgraded.
|
|
|
|
|
CandidateMap mElementCreationCallbacksUpgradeCandidatesMap;
|
|
|
|
|
|
2016-07-25 00:38:00 +03:00
|
|
|
|
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
|
|
|
|
|
2016-09-01 10:11:32 +03:00
|
|
|
|
// It is used to prevent reentrant invocations of element definition.
|
|
|
|
|
bool mIsCustomDefinitionRunning;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
class MOZ_RAII AutoSetRunningFlag final {
|
|
|
|
|
public:
|
2016-10-12 10:26:02 +03:00
|
|
|
|
explicit AutoSetRunningFlag(CustomElementRegistry* aRegistry)
|
2016-09-01 10:11:32 +03:00
|
|
|
|
: mRegistry(aRegistry) {
|
|
|
|
|
MOZ_ASSERT(!mRegistry->mIsCustomDefinitionRunning,
|
|
|
|
|
"IsCustomDefinitionRunning flag should be initially false");
|
|
|
|
|
mRegistry->mIsCustomDefinitionRunning = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~AutoSetRunningFlag() { mRegistry->mIsCustomDefinitionRunning = false; }
|
|
|
|
|
|
|
|
|
|
private:
|
2016-10-12 10:26:02 +03:00
|
|
|
|
CustomElementRegistry* mRegistry;
|
2016-09-01 10:11:32 +03:00
|
|
|
|
};
|
|
|
|
|
|
2018-08-15 13:31:16 +03:00
|
|
|
|
int32_t InferNamespace(JSContext* aCx, JS::Handle<JSObject*> constructor);
|
|
|
|
|
|
2016-07-25 00:38:00 +03:00
|
|
|
|
public:
|
|
|
|
|
nsISupports* GetParentObject() const;
|
|
|
|
|
|
2017-11-27 11:10:27 +03:00
|
|
|
|
DocGroup* GetDocGroup() const;
|
|
|
|
|
|
2016-07-25 00:38:00 +03:00
|
|
|
|
virtual JSObject* WrapObject(JSContext* aCx,
|
|
|
|
|
JS::Handle<JSObject*> aGivenProto) override;
|
|
|
|
|
|
2018-05-18 23:18:19 +03:00
|
|
|
|
void Define(JSContext* aCx, const nsAString& aName,
|
2019-04-10 18:45:11 +03:00
|
|
|
|
CustomElementConstructor& aFunctionConstructor,
|
2016-07-25 00:38:00 +03:00
|
|
|
|
const ElementDefinitionOptions& aOptions, ErrorResult& aRv);
|
|
|
|
|
|
|
|
|
|
void Get(JSContext* cx, const nsAString& name,
|
2016-09-22 02:40:00 +03:00
|
|
|
|
JS::MutableHandle<JS::Value> aRetVal);
|
2016-07-25 00:38:00 +03:00
|
|
|
|
|
2016-10-07 09:16:00 +03:00
|
|
|
|
already_AddRefed<Promise> WhenDefined(const nsAString& aName,
|
|
|
|
|
ErrorResult& aRv);
|
2018-05-11 21:23:31 +03:00
|
|
|
|
|
|
|
|
|
// 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);
|
2018-06-20 02:06:33 +03:00
|
|
|
|
|
|
|
|
|
void Upgrade(nsINode& aRoot);
|
2016-07-25 00:38:00 +03:00
|
|
|
|
};
|
|
|
|
|
|
2017-01-06 02:38:00 +03:00
|
|
|
|
class MOZ_RAII AutoCEReaction final {
|
|
|
|
|
public:
|
2017-11-17 12:44:18 +03:00
|
|
|
|
// 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) {
|
2017-11-28 11:58:50 +03:00
|
|
|
|
mIsElementQueuePushedForPreviousRecursionDepth =
|
|
|
|
|
mReactionsStack->EnterCEReactions();
|
2017-01-06 02:38:00 +03:00
|
|
|
|
}
|
2017-11-28 11:58:50 +03:00
|
|
|
|
|
2019-04-10 15:51:15 +03:00
|
|
|
|
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because this is called from Maybe<>.reset().
|
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY ~AutoCEReaction() {
|
2017-11-28 11:58:50 +03:00
|
|
|
|
mReactionsStack->LeaveCEReactions(
|
|
|
|
|
mCx, mIsElementQueuePushedForPreviousRecursionDepth);
|
2017-01-06 02:38:00 +03:00
|
|
|
|
}
|
2017-11-28 11:58:50 +03:00
|
|
|
|
|
2017-01-06 02:38:00 +03:00
|
|
|
|
private:
|
2019-04-09 16:37:23 +03:00
|
|
|
|
const RefPtr<CustomElementReactionsStack> mReactionsStack;
|
2017-11-17 12:44:18 +03:00
|
|
|
|
JSContext* mCx;
|
2017-11-28 11:58:50 +03:00
|
|
|
|
bool mIsElementQueuePushedForPreviousRecursionDepth;
|
2017-01-06 02:38:00 +03:00
|
|
|
|
};
|
|
|
|
|
|
2016-07-25 00:38:00 +03:00
|
|
|
|
} // namespace dom
|
|
|
|
|
} // namespace mozilla
|
|
|
|
|
|
2016-10-12 10:26:02 +03:00
|
|
|
|
#endif // mozilla_dom_CustomElementRegistry_h
|