Implement Custom Elements v1.

master
Fedor 2020-03-12 20:41:43 +03:00
parent cd5cc91f9d
commit 99fd13b6dd
265 changed files with 18152 additions and 13653 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,16 @@
#ifndef mozilla_dom_CustomElementRegistry_h #ifndef mozilla_dom_CustomElementRegistry_h
#define mozilla_dom_CustomElementRegistry_h #define mozilla_dom_CustomElementRegistry_h
#include "js/GCHashTable.h"
#include "js/TypeDecls.h" #include "js/TypeDecls.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/BindingDeclarations.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/FunctionBinding.h" #include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/WebComponentsBinding.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGenericHTMLElement.h"
#include "nsWrapperCache.h"
class nsDocument; class nsDocument;
@ -22,8 +25,8 @@ namespace dom {
struct CustomElementData; struct CustomElementData;
struct ElementDefinitionOptions; struct ElementDefinitionOptions;
struct LifecycleCallbacks;
class CallbackFunction; class CallbackFunction;
class CustomElementReaction;
class Function; class Function;
class Promise; class Promise;
@ -32,6 +35,13 @@ struct LifecycleCallbackArgs
nsString name; nsString name;
nsString oldValue; nsString oldValue;
nsString newValue; nsString newValue;
nsString namespaceURI;
};
struct LifecycleAdoptedCallbackArgs
{
nsCOMPtr<nsIDocument> mOldDocument;
nsCOMPtr<nsIDocument> mNewDocument;
}; };
class CustomElementCallback class CustomElementCallback
@ -39,8 +49,7 @@ class CustomElementCallback
public: public:
CustomElementCallback(Element* aThisObject, CustomElementCallback(Element* aThisObject,
nsIDocument::ElementCallbackType aCallbackType, nsIDocument::ElementCallbackType aCallbackType,
CallbackFunction* aCallback, CallbackFunction* aCallback);
CustomElementData* aOwnerData);
void Traverse(nsCycleCollectionTraversalCallback& aCb) const; void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
void Call(); void Call();
void SetArgs(LifecycleCallbackArgs& aArgs) void SetArgs(LifecycleCallbackArgs& aArgs)
@ -50,6 +59,13 @@ public:
mArgs = aArgs; mArgs = aArgs;
} }
void SetAdoptedCallbackArgs(LifecycleAdoptedCallbackArgs& aAdoptedCallbackArgs)
{
MOZ_ASSERT(mType == nsIDocument::eAdopted,
"Arguments are only used by adopted callback.");
mAdoptedCallbackArgs = aAdoptedCallbackArgs;
}
private: private:
// The this value to use for invocation of the callback. // The this value to use for invocation of the callback.
RefPtr<Element> mThisObject; RefPtr<Element> mThisObject;
@ -59,9 +75,19 @@ private:
// Arguments to be passed to the callback, // Arguments to be passed to the callback,
// used by the attribute changed callback. // used by the attribute changed callback.
LifecycleCallbackArgs mArgs; LifecycleCallbackArgs mArgs;
// CustomElementData that contains this callback in the LifecycleAdoptedCallbackArgs mAdoptedCallbackArgs;
// callback queue. };
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 // Each custom element has an associated callback queue and an element is
@ -70,63 +96,299 @@ struct CustomElementData
{ {
NS_INLINE_DECL_REFCOUNTING(CustomElementData) NS_INLINE_DECL_REFCOUNTING(CustomElementData)
explicit CustomElementData(nsIAtom* aType); // https://dom.spec.whatwg.org/#concept-element-custom-element-state
// Objects in this array are transient and empty after each microtask // CustomElementData is only created on the element which is a custom element
// checkpoint. // or an upgrade candidate, so the state of an element without
nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue; // CustomElementData is "uncustomized".
// Custom element type, for <button is="x-button"> or <x-button> enum class State {
// this would be x-button. eUndefined,
nsCOMPtr<nsIAtom> mType; eFailed,
// The callback that is next to be processed upon calling RunCallbackQueue. eCustom
int32_t mCurrentCallback; };
// Element is being created flag as described in the custom elements spec.
bool mElementIsBeingCreated;
// Flag to determine if the created callback has been invoked, thus it
// determines if other callbacks can be enqueued.
bool mCreatedCallbackInvoked;
// The microtask level associated with the callbacks in the callback queue,
// it is used to determine if a new queue needs to be pushed onto the
// processing stack.
int32_t mAssociatedMicroTask;
// Empties the callback queue. explicit CustomElementData(nsIAtom* aType);
void RunCallbackQueue(); CustomElementData(nsIAtom* aType, State aState);
// 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) its 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;
void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
CustomElementDefinition* GetCustomElementDefinition();
nsIAtom* GetCustomElementType();
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
void Unlink();
private: private:
virtual ~CustomElementData() {} virtual ~CustomElementData() {}
// Custom element type, for <button is="x-button"> or <x-button>
// this would be x-button.
RefPtr<nsIAtom> mType;
RefPtr<CustomElementDefinition> mCustomElementDefinition;
}; };
#define ALEADY_CONSTRUCTED_MARKER nullptr
// The required information for a custom element as defined in: // The required information for a custom element as defined in:
// https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
struct CustomElementDefinition struct CustomElementDefinition
{ {
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
CustomElementDefinition(nsIAtom* aType, CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName, nsIAtom* aLocalName,
JSObject* aConstructor, Function* aConstructor,
nsCOMArray<nsIAtom>&& aObservedAttributes,
JSObject* aPrototype, JSObject* aPrototype,
mozilla::dom::LifecycleCallbacks* aCallbacks, mozilla::dom::LifecycleCallbacks* aCallbacks,
uint32_t aDocOrder); uint32_t aDocOrder);
// The type (name) for this custom element. // The type (name) for this custom element, for <button is="x-foo"> or <x-foo>
// this would be x-foo.
nsCOMPtr<nsIAtom> mType; nsCOMPtr<nsIAtom> mType;
// The localname to (e.g. <button is=type> -- this would be button). // The localname to (e.g. <button is=type> -- this would be button).
nsCOMPtr<nsIAtom> mLocalName; nsCOMPtr<nsIAtom> mLocalName;
// The custom element constructor. // The custom element constructor.
JS::Heap<JSObject *> mConstructor; RefPtr<CustomElementConstructor> mConstructor;
// The list of attributes that this custom element observes.
nsCOMArray<nsIAtom> mObservedAttributes;
// The prototype to use for new custom elements of this type. // The prototype to use for new custom elements of this type.
JS::Heap<JSObject *> mPrototype; JS::Heap<JSObject *> mPrototype;
// The lifecycle callbacks to call for this custom element. // The lifecycle callbacks to call for this custom element.
nsAutoPtr<mozilla::dom::LifecycleCallbacks> mCallbacks; UniquePtr<mozilla::dom::LifecycleCallbacks> mCallbacks;
// A construction stack. // A construction stack. Use nullptr to represent an "already constructed marker".
// TODO: Bug 1287348 - Implement construction stack for upgrading an element nsTArray<RefPtr<nsGenericHTMLElement>> mConstructionStack;
// The document custom element order. // The document custom element order.
uint32_t mDocOrder; uint32_t mDocOrder;
bool IsCustomBuiltIn()
{
return mType != mLocalName;
}
bool IsInObservedAttributeList(nsIAtom* aName)
{
if (mObservedAttributes.IsEmpty()) {
return false;
}
return mObservedAttributes.Contains(aName);
}
private:
~CustomElementDefinition() {}
};
class CustomElementReaction
{
public:
virtual ~CustomElementReaction() = default;
virtual void Invoke(Element* aElement, ErrorResult& aRv) = 0;
virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const
{
}
#if DEBUG
bool IsUpgradeReaction()
{
return mIsUpgradeReaction;
}
protected:
bool mIsUpgradeReaction = false;
#endif
};
class CustomElementUpgradeReaction final : public CustomElementReaction
{
public:
explicit CustomElementUpgradeReaction(CustomElementDefinition* aDefinition)
: mDefinition(aDefinition)
{
#if DEBUG
mIsUpgradeReaction = true;
#endif
}
private:
virtual void Invoke(Element* aElement, ErrorResult& aRv) override;
CustomElementDefinition* mDefinition;
};
class CustomElementCallbackReaction final : public CustomElementReaction
{
public:
explicit CustomElementCallbackReaction(UniquePtr<CustomElementCallback> aCustomElementCallback)
: mCustomElementCallback(Move(aCustomElementCallback))
{
}
virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const override
{
mCustomElementCallback->Traverse(aCb);
}
private:
virtual void Invoke(Element* aElement, ErrorResult& aRv) override;
UniquePtr<CustomElementCallback> mCustomElementCallback;
};
// https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack
class CustomElementReactionsStack
{
public:
NS_INLINE_DECL_REFCOUNTING(CustomElementReactionsStack)
CustomElementReactionsStack()
: mIsBackupQueueProcessing(false)
, mRecursionDepth(0)
, mIsElementQueuePushedForCurrentRecursionDepth(false)
{
}
// 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 1 for the auto size here is based on gut feeling.
typedef AutoTArray<RefPtr<Element>, 1> ElementQueue;
/**
* 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);
/**
* Enqueue a custom element callback reaction
* https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-callback-reaction
*/
void EnqueueCallbackReaction(Element* aElement,
UniquePtr<CustomElementCallback> aCustomElementCallback);
/**
* [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.
*/
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());
}
private:
~CustomElementReactionsStack() {};
/**
* 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.
*/
void PopAndInvokeElementQueue();
// The choice of 8 for the auto size here is based on gut feeling.
AutoTArray<UniquePtr<ElementQueue>, 8> mReactionsStack;
ElementQueue mBackupQueue;
// https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue
bool mIsBackupQueueProcessing;
void InvokeBackupQueue();
/**
* Invoke custom element reactions
* https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions
*/
void InvokeReactions(ElementQueue* aElementQueue, nsIGlobalObject* aGlobal);
void Enqueue(Element* aElement, CustomElementReaction* aReaction);
// 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;
}
virtual void Run(AutoSlowOperation& aAso) override
{
mReactionStack->InvokeBackupQueue();
mReactionStack->mIsBackupQueueProcessing = false;
}
private:
RefPtr<CustomElementReactionsStack> mReactionStack;
};
}; };
class CustomElementRegistry final : public nsISupports, class CustomElementRegistry final : public nsISupports,
@ -142,36 +404,33 @@ public:
public: public:
static bool IsCustomElementEnabled(JSContext* aCx = nullptr, static bool IsCustomElementEnabled(JSContext* aCx = nullptr,
JSObject* aObject = nullptr); JSObject* aObject = nullptr);
static already_AddRefed<CustomElementRegistry> Create(nsPIDOMWindowInner* aWindow);
static void ProcessTopElementQueue();
static void XPCOMShutdown(); explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
/** /**
* Looking up a custom element definition. * Looking up a custom element definition.
* https://html.spec.whatwg.org/#look-up-a-custom-element-definition * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
*/ */
CustomElementDefinition* LookupCustomElementDefinition( CustomElementDefinition* LookupCustomElementDefinition(
const nsAString& aLocalName, const nsAString* aIs = nullptr) const; nsIAtom* aNameAtom, nsIAtom* aTypeAtom) const;
/** CustomElementDefinition* LookupCustomElementDefinition(
* Enqueue created callback or register upgrade candidate for JSContext* aCx, JSObject *aConstructor) const;
* newly created custom elements, possibly extending an existing type.
* ex. <x-button>, <button is="x-button> (type extension)
*/
void SetupCustomElement(Element* aElement, const nsAString* aTypeExtension);
void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
Element* aCustomElement, Element* aCustomElement,
LifecycleCallbackArgs* aArgs, LifecycleCallbackArgs* aArgs,
CustomElementDefinition* aDefinition); LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
CustomElementDefinition* aDefinition);
void GetCustomPrototype(nsIAtom* aAtom, void GetCustomPrototype(nsIAtom* aAtom,
JS::MutableHandle<JSObject*> aPrototype); JS::MutableHandle<JSObject*> aPrototype);
private: /**
explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow); * Upgrade an element.
~CustomElementRegistry(); * https://html.spec.whatwg.org/multipage/scripting.html#upgrades
*/
static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv);
/** /**
* Registers an unresolved custom element that is a candidate for * Registers an unresolved custom element that is a candidate for
@ -184,23 +443,48 @@ private:
void RegisterUnresolvedElement(Element* aElement, void RegisterUnresolvedElement(Element* aElement,
nsIAtom* aTypeName = nullptr); nsIAtom* aTypeName = nullptr);
void UpgradeCandidates(JSContext* aCx, /**
nsIAtom* aKey, * Unregister an unresolved custom element that is a candidate for
CustomElementDefinition* aDefinition); * upgrade when a custom element is removed from tree.
*/
void UnregisterUnresolvedElement(Element* aElement,
nsIAtom* aTypeName = nullptr);
private:
~CustomElementRegistry();
typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition> static UniquePtr<CustomElementCallback> CreateCustomElementCallback(
nsIDocument::ElementCallbackType aType, Element* aCustomElement,
LifecycleCallbackArgs* aArgs,
LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
CustomElementDefinition* aDefinition);
void UpgradeCandidates(nsIAtom* aKey,
CustomElementDefinition* aDefinition,
ErrorResult& aRv);
typedef nsRefPtrHashtable<nsISupportsHashKey, CustomElementDefinition>
DefinitionMap; DefinitionMap;
typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>> typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
CandidateMap; CandidateMap;
typedef JS::GCHashMap<JS::Heap<JSObject*>,
nsCOMPtr<nsIAtom>,
js::MovableCellHasher<JS::Heap<JSObject*>>,
js::SystemAllocPolicy> ConstructorMap;
// Hashtable for custom element definitions in web components. // Hashtable for custom element definitions in web components.
// Custom prototypes are stored in the compartment where // Custom prototypes are stored in the compartment where
// registerElement was called. // registerElement was called.
DefinitionMap mCustomDefinitions; DefinitionMap mCustomDefinitions;
// 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;
typedef nsRefPtrHashtable<nsISupportsHashKey, Promise> typedef nsRefPtrHashtable<nsISupportsHashKey, Promise>
WhenDefinedPromiseMap; WhenDefinedPromiseMap;
WhenDefinedPromiseMap mWhenDefinedPromiseMap; WhenDefinedPromiseMap mWhenDefinedPromiseMap;
// The "upgrade candidates map" from the web components spec. Maps from a // 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 // namespace id and local name to a list of elements to upgrade if that
// element is registered as a custom element. // element is registered as a custom element.
@ -208,14 +492,6 @@ private:
nsCOMPtr<nsPIDOMWindowInner> mWindow; nsCOMPtr<nsPIDOMWindowInner> mWindow;
// Array representing the processing stack in the custom elements
// specification. The processing stack is conceptually a stack of
// element queues. Each queue is represented by a sequence of
// CustomElementData in this array, separated by nullptr that
// represent the boundaries of the items in the stack. The first
// queue in the stack is the base element queue.
static mozilla::Maybe<nsTArray<RefPtr<CustomElementData>>> sProcessingStack;
// It is used to prevent reentrant invocations of element definition. // It is used to prevent reentrant invocations of element definition.
bool mIsCustomDefinitionRunning; bool mIsCustomDefinitionRunning;
@ -252,6 +528,31 @@ 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:
// 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();
}
~AutoCEReaction()
{
mReactionsStack->LeaveCEReactions(
mCx, mIsElementQueuePushedForPreviousRecursionDepth);
}
private:
RefPtr<CustomElementReactionsStack> mReactionsStack;
JSContext* mCx;
bool mIsElementQueuePushedForPreviousRecursionDepth;
};
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -46,6 +46,9 @@ DocGroup::DocGroup(TabGroup* aTabGroup, const nsACString& aKey)
DocGroup::~DocGroup() DocGroup::~DocGroup()
{ {
MOZ_ASSERT(mDocuments.IsEmpty()); MOZ_ASSERT(mDocuments.IsEmpty());
if (!NS_IsMainThread()) {
NS_ReleaseOnMainThread(mReactionsStack.forget());
}
mTabGroup->mDocGroups.RemoveEntry(mKey); mTabGroup->mDocGroups.RemoveEntry(mKey);
} }

View File

@ -14,6 +14,7 @@
#include "nsString.h" #include "nsString.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/dom/CustomElementRegistry.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -52,6 +53,14 @@ public:
{ {
return mTabGroup; return mTabGroup;
} }
mozilla::dom::CustomElementReactionsStack* CustomElementReactionsStack()
{
if (!mReactionsStack) {
mReactionsStack = new mozilla::dom::CustomElementReactionsStack();
}
return mReactionsStack;
}
void RemoveDocument(nsIDocument* aWindow); void RemoveDocument(nsIDocument* aWindow);
// Iterators for iterating over every document within the DocGroup // Iterators for iterating over every document within the DocGroup
@ -71,6 +80,7 @@ private:
nsCString mKey; nsCString mKey;
RefPtr<TabGroup> mTabGroup; RefPtr<TabGroup> mTabGroup;
nsTArray<nsIDocument*> mDocuments; nsTArray<nsIDocument*> mDocuments;
RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
}; };
} // namespace dom } // namespace dom

View File

@ -479,9 +479,13 @@ Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
if (data) { if (data) {
// If this is a registered custom element then fix the prototype. // If this is a registered custom element then fix the prototype.
nsContentUtils::GetCustomPrototype(OwnerDoc(), NodeInfo()->NamespaceID(), nsContentUtils::GetCustomPrototype(OwnerDoc(), NodeInfo()->NamespaceID(),
data->mType, &customProto); data->GetCustomElementType(), &customProto);
if (customProto && if (customProto &&
NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(customProto))) { NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(customProto))) {
// The custom element prototype could be in different compartment.
if (!JS_WrapObject(aCx, &customProto)) {
return nullptr;
}
// Just go ahead and create with the right proto up front. Set // Just go ahead and create with the right proto up front. Set
// customProto to null to flag that we don't need to do any post-facto // customProto to null to flag that we don't need to do any post-facto
// proto fixups here. // proto fixups here.
@ -1595,7 +1599,7 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
#endif #endif
{ {
if (aBindingParent) { if (aBindingParent) {
nsDOMSlots *slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
slots->mBindingParent = aBindingParent; // Weak, so no addref happens. slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
} }
@ -1618,7 +1622,7 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
} }
ShadowRoot* parentContainingShadow = aParent->GetContainingShadow(); ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
if (parentContainingShadow) { if (parentContainingShadow) {
DOMSlots()->mContainingShadow = parentContainingShadow; ExtendedDOMSlots()->mContainingShadow = parentContainingShadow;
} }
} }
@ -1684,14 +1688,17 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
SetSubtreeRootPointer(aParent->SubtreeRoot()); SetSubtreeRootPointer(aParent->SubtreeRoot());
} }
nsIDocument* composedDoc = GetComposedDoc(); if (CustomElementRegistry::IsCustomElementEnabled() && IsInComposedDoc()) {
if (composedDoc) { // Connected callback must be enqueued whenever a custom element becomes
// Attached callback must be enqueued whenever custom element is inserted into a // connected.
// document and this document has a browsing context. CustomElementData* data = GetCustomElementData();
if (GetCustomElementData() && composedDoc->GetDocShell()) { if (data) {
// Enqueue an attached callback for the custom element. if (data->mState == CustomElementData::State::eCustom) {
nsContentUtils::EnqueueLifecycleCallback( nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eConnected, this);
composedDoc, nsIDocument::eAttached, this); } else {
// Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
nsContentUtils::TryToUpgradeElement(this);
}
} }
} }
@ -1986,12 +1993,21 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
document->ClearBoxObjectFor(this); document->ClearBoxObjectFor(this);
// Detached must be enqueued whenever custom element is removed from // Disconnected must be enqueued whenever a connected custom element becomes
// the document and this document has a browsing context. // disconnected.
if (GetCustomElementData() && document->GetDocShell()) { if (CustomElementRegistry::IsCustomElementEnabled()) {
// Enqueue a detached callback for the custom element. CustomElementData* data = GetCustomElementData();
nsContentUtils::EnqueueLifecycleCallback( if (data) {
document, nsIDocument::eDetached, this); if (data->mState == CustomElementData::State::eCustom) {
nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eDisconnected,
this);
} else {
// Remove an unresolved custom element that is a candidate for
// upgrade when a custom element is disconnected.
// We will make sure it's shadow-including tree order in bug 1326028.
nsContentUtils::UnregisterUnresolvedElement(this);
}
}
} }
} }
@ -2007,7 +2023,7 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
} }
#endif #endif
nsDOMSlots* slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
if (clearBindingParent) { if (clearBindingParent) {
slots->mBindingParent = nullptr; slots->mBindingParent = nullptr;
@ -2055,7 +2071,7 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
nsICSSDeclaration* nsICSSDeclaration*
Element::GetSMILOverrideStyle() Element::GetSMILOverrideStyle()
{ {
Element::nsDOMSlots *slots = DOMSlots(); Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
if (!slots->mSMILOverrideStyle) { if (!slots->mSMILOverrideStyle) {
slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true); slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true);
@ -2067,7 +2083,7 @@ Element::GetSMILOverrideStyle()
DeclarationBlock* DeclarationBlock*
Element::GetSMILOverrideStyleDeclaration() Element::GetSMILOverrideStyleDeclaration()
{ {
Element::nsDOMSlots *slots = GetExistingDOMSlots(); Element::nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr; return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr;
} }
@ -2075,7 +2091,7 @@ nsresult
Element::SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration, Element::SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration,
bool aNotify) bool aNotify)
{ {
Element::nsDOMSlots *slots = DOMSlots(); Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
slots->mSMILOverrideStyleDeclaration = aDeclaration; slots->mSMILOverrideStyleDeclaration = aDeclaration;
@ -2586,19 +2602,32 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
UpdateState(aNotify); UpdateState(aNotify);
nsIDocument* ownerDoc = OwnerDoc(); if (CustomElementRegistry::IsCustomElementEnabled()) {
if (ownerDoc && GetCustomElementData()) { if (CustomElementData* data = GetCustomElementData()) {
nsCOMPtr<nsIAtom> oldValueAtom = oldValue->GetAsAtom(); if (CustomElementDefinition* definition =
nsCOMPtr<nsIAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom(); nsContentUtils::GetElementDefinitionIfObservingAttr(this,
LifecycleCallbackArgs args = { data->GetCustomElementType(),
nsDependentAtomString(aName), aName)) {
aModType == nsIDOMMutationEvent::ADDITION ? MOZ_ASSERT(data->mState == CustomElementData::State::eCustom,
NullString() : nsDependentAtomString(oldValueAtom), "AttributeChanged callback should fire only if "
nsDependentAtomString(newValueAtom) "custom element state is custom");
}; nsCOMPtr<nsIAtom> oldValueAtom = oldValue->GetAsAtom();
nsCOMPtr<nsIAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
nsAutoString ns;
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
nsContentUtils::EnqueueLifecycleCallback( LifecycleCallbackArgs args = {
ownerDoc, nsIDocument::eAttributeChanged, this, &args); nsDependentAtomString(aName),
aModType == nsIDOMMutationEvent::ADDITION ?
NullString() : nsDependentAtomString(oldValueAtom),
nsDependentAtomString(newValueAtom),
(ns.IsEmpty() ? NullString() : ns)
};
nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
this, &args, nullptr, definition);
}
}
} }
if (aCallAfterSetAttr) { if (aCallAfterSetAttr) {
@ -2843,17 +2872,30 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
UpdateState(aNotify); UpdateState(aNotify);
nsIDocument* ownerDoc = OwnerDoc(); if (CustomElementRegistry::IsCustomElementEnabled()) {
if (ownerDoc && GetCustomElementData()) { if (CustomElementData* data = GetCustomElementData()) {
nsCOMPtr<nsIAtom> oldValueAtom = oldValue.GetAsAtom(); if (CustomElementDefinition* definition =
LifecycleCallbackArgs args = { nsContentUtils::GetElementDefinitionIfObservingAttr(this,
nsDependentAtomString(aName), data->GetCustomElementType(),
nsDependentAtomString(oldValueAtom), aName)) {
NullString() MOZ_ASSERT(data->mState == CustomElementData::State::eCustom,
}; "AttributeChanged callback should fire only if "
"custom element state is custom");
nsAutoString ns;
nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
nsContentUtils::EnqueueLifecycleCallback( nsCOMPtr<nsIAtom> oldValueAtom = oldValue.GetAsAtom();
ownerDoc, nsIDocument::eAttributeChanged, this, &args); LifecycleCallbackArgs args = {
nsDependentAtomString(aName),
nsDependentAtomString(oldValueAtom),
NullString(),
(ns.IsEmpty() ? NullString() : ns)
};
nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
this, &args, nullptr, definition);
}
}
} }
if (aNotify) { if (aNotify) {
@ -3988,7 +4030,7 @@ Element::ClearDataset()
nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>* nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
Element::RegisteredIntersectionObservers() Element::RegisteredIntersectionObservers()
{ {
nsDOMSlots* slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
return &slots->mRegisteredIntersectionObservers; return &slots->mRegisteredIntersectionObservers;
} }
@ -4037,3 +4079,31 @@ Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32
} }
return false; return false;
} }
void
Element::SetCustomElementData(CustomElementData* aData)
{
nsExtendedDOMSlots *slots = ExtendedDOMSlots();
MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
slots->mCustomElementData = aData;
}
CustomElementDefinition*
Element::GetCustomElementDefinition() const
{
CustomElementData* data = GetCustomElementData();
if (!data) {
return nullptr;
}
return data->GetCustomElementDefinition();
}
void
Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition)
{
CustomElementData* data = GetCustomElementData();
MOZ_ASSERT(data);
data->SetCustomElementDefinition(aDefinition);
}

View File

@ -390,6 +390,45 @@ public:
Directionality GetComputedDirectionality() const; Directionality GetComputedDirectionality() const;
/**
* Gets the custom element data used by web components custom element.
* Custom element data is created at the first attempt to enqueue a callback.
*
* @return The custom element data or null if none.
*/
inline CustomElementData* GetCustomElementData() const
{
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) {
return slots->mCustomElementData;
}
return nullptr;
}
/**
* Sets the custom element data, ownership of the
* callback data is taken by this element.
*
* @param aData The custom element data.
*/
void SetCustomElementData(CustomElementData* aData);
/**
* Gets the custom element definition used by web components custom element.
*
* @return The custom element definition or null if element is not a custom
* element or custom element is not defined yet.
*/
CustomElementDefinition* GetCustomElementDefinition() const;
/**
* Sets the custom element definition, called when custom element is created
* or upgraded.
*
* @param aDefinition The custom element definition.
*/
void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
protected: protected:
/** /**
* Method to get the _intrinsic_ content state of this element. This is the * Method to get the _intrinsic_ content state of this element. This is the
@ -814,7 +853,7 @@ public:
ShadowRoot *FastGetShadowRoot() const ShadowRoot *FastGetShadowRoot() const
{ {
nsDOMSlots* slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
return slots ? slots->mShadowRoot.get() : nullptr; return slots ? slots->mShadowRoot.get() : nullptr;
} }

View File

@ -530,8 +530,7 @@ nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
FragmentOrElement::nsDOMSlots::nsDOMSlots() FragmentOrElement::nsDOMSlots::nsDOMSlots()
: nsINode::nsSlots(), : nsINode::nsSlots(),
mDataset(nullptr), mDataset(nullptr)
mBindingParent(nullptr)
{ {
} }
@ -543,84 +542,104 @@ FragmentOrElement::nsDOMSlots::~nsDOMSlots()
} }
void void
FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL) FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
{ {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
cb.NoteXPCOMChild(mStyle.get()); cb.NoteXPCOMChild(mStyle.get());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
cb.NoteXPCOMChild(mSMILOverrideStyle.get());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
cb.NoteXPCOMChild(mAttributeMap.get()); cb.NoteXPCOMChild(mAttributeMap.get());
if (aIsXUL) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
cb.NoteXPCOMChild(mControllers);
}
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
cb.NoteXPCOMChild(mXBLInsertionParent.get());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList)); cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mLabelsList");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mLabelsList));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
cb.NoteXPCOMChild(mClassList.get()); cb.NoteXPCOMChild(mClassList.get());
if (mCustomElementData) { if (!mExtendedSlots) {
for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) { return;
mCustomElementData->mCallbackQueue[i]->Traverse(cb);
}
} }
for (auto iter = mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mSMILOverrideStyle");
cb.NoteXPCOMChild(mExtendedSlots->mSMILOverrideStyle.get());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mControllers");
cb.NoteXPCOMChild(mExtendedSlots->mControllers);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mLabelsList");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*,mExtendedSlots-> mLabelsList));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mShadowRoot");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mShadowRoot));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mContainingShadow");
cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mContainingShadow));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mXBLBinding");
cb.NoteNativeChild(mExtendedSlots->mXBLBinding,
NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mXBLInsertionParent");
cb.NoteXPCOMChild(mExtendedSlots->mXBLInsertionParent.get());
if (mExtendedSlots->mCustomElementData) {
mExtendedSlots->mCustomElementData->Traverse(cb);
}
for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
!iter.Done(); iter.Next()) {
DOMIntersectionObserver* observer = iter.Key(); DOMIntersectionObserver* observer = iter.Key();
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mRegisteredIntersectionObservers[i]"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mExtendedSlots->mRegisteredIntersectionObservers[i]");
cb.NoteXPCOMChild(observer); cb.NoteXPCOMChild(observer);
} }
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mFrameLoaderOrOpener");
cb.NoteXPCOMChild(mExtendedSlots->mFrameLoaderOrOpener);
} }
void void
FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL) FragmentOrElement::nsDOMSlots::Unlink()
{ {
mStyle = nullptr; mStyle = nullptr;
mSMILOverrideStyle = nullptr;
if (mAttributeMap) { if (mAttributeMap) {
mAttributeMap->DropReference(); mAttributeMap->DropReference();
mAttributeMap = nullptr; mAttributeMap = nullptr;
} }
if (aIsXUL)
NS_IF_RELEASE(mControllers);
MOZ_ASSERT(!mXBLBinding);
mXBLInsertionParent = nullptr;
mShadowRoot = nullptr;
mContainingShadow = nullptr;
mChildrenList = nullptr; mChildrenList = nullptr;
mLabelsList = nullptr;
mCustomElementData = nullptr;
mClassList = nullptr; mClassList = nullptr;
mRegisteredIntersectionObservers.Clear();
if (!mExtendedSlots) {
return;
}
mExtendedSlots->mSMILOverrideStyle = nullptr;
mExtendedSlots->mControllers = nullptr;
mExtendedSlots->mLabelsList = nullptr;
mExtendedSlots->mShadowRoot = nullptr;
mExtendedSlots->mContainingShadow = nullptr;
MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
mExtendedSlots->mXBLInsertionParent = nullptr;
if (mExtendedSlots->mCustomElementData) {
mExtendedSlots->mCustomElementData->Unlink();
mExtendedSlots->mCustomElementData = nullptr;
}
mExtendedSlots->mRegisteredIntersectionObservers.Clear();
nsCOMPtr<nsIFrameLoader> frameLoader =
do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener);
if (frameLoader) {
static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
}
mExtendedSlots->mFrameLoaderOrOpener = nullptr;
} }
size_t size_t
FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{ {
size_t n = aMallocSizeOf(this); size_t n = aMallocSizeOf(this);
if (mExtendedSlots) {
n += aMallocSizeOf(mExtendedSlots.get());
}
if (mAttributeMap) { if (mAttributeMap) {
n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf); n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
@ -641,6 +660,19 @@ FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) c
return n; return n;
} }
FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots()
: mBindingParent(nullptr)
{
}
FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots()
{
nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(mFrameLoaderOrOpener);
if (frameLoader) {
static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
}
}
FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsIContent(aNodeInfo) : nsIContent(aNodeInfo)
{ {
@ -962,7 +994,7 @@ FragmentOrElement::IsLink(nsIURI** aURI) const
nsIContent* nsIContent*
FragmentOrElement::GetBindingParent() const FragmentOrElement::GetBindingParent() const
{ {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
return slots->mBindingParent; return slots->mBindingParent;
@ -974,7 +1006,7 @@ nsXBLBinding*
FragmentOrElement::GetXBLBinding() const FragmentOrElement::GetXBLBinding() const
{ {
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
return slots->mXBLBinding; return slots->mXBLBinding;
} }
@ -1009,11 +1041,11 @@ FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
if (aBinding) { if (aBinding) {
SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
nsDOMSlots *slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
slots->mXBLBinding = aBinding; slots->mXBLBinding = aBinding;
bindingManager->AddBoundContent(this); bindingManager->AddBoundContent(this);
} else { } else {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
slots->mXBLBinding = nullptr; slots->mXBLBinding = nullptr;
} }
@ -1028,7 +1060,7 @@ nsIContent*
FragmentOrElement::GetXBLInsertionParent() const FragmentOrElement::GetXBLInsertionParent() const
{ {
if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
return slots->mXBLInsertionParent; return slots->mXBLInsertionParent;
} }
@ -1040,7 +1072,7 @@ FragmentOrElement::GetXBLInsertionParent() const
ShadowRoot* ShadowRoot*
FragmentOrElement::GetContainingShadow() const FragmentOrElement::GetContainingShadow() const
{ {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
return slots->mContainingShadow; return slots->mContainingShadow;
} }
@ -1050,21 +1082,21 @@ FragmentOrElement::GetContainingShadow() const
void void
FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot) FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
{ {
nsDOMSlots *slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
slots->mShadowRoot = aShadowRoot; slots->mShadowRoot = aShadowRoot;
} }
nsTArray<nsIContent*>& nsTArray<nsIContent*>&
FragmentOrElement::DestInsertionPoints() FragmentOrElement::DestInsertionPoints()
{ {
nsDOMSlots *slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
return slots->mDestInsertionPoints; return slots->mDestInsertionPoints;
} }
nsTArray<nsIContent*>* nsTArray<nsIContent*>*
FragmentOrElement::GetExistingDestInsertionPoints() const FragmentOrElement::GetExistingDestInsertionPoints() const
{ {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
return &slots->mDestInsertionPoints; return &slots->mDestInsertionPoints;
} }
@ -1075,35 +1107,17 @@ void
FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent) FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
{ {
if (aContent) { if (aContent) {
nsDOMSlots *slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
slots->mXBLInsertionParent = aContent; slots->mXBLInsertionParent = aContent;
} else { } else {
nsDOMSlots *slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots) { if (slots) {
slots->mXBLInsertionParent = nullptr; slots->mXBLInsertionParent = nullptr;
} }
} }
} }
CustomElementData*
FragmentOrElement::GetCustomElementData() const
{
nsDOMSlots *slots = GetExistingDOMSlots();
if (slots) {
return slots->mCustomElementData;
}
return nullptr;
}
void
FragmentOrElement::SetCustomElementData(CustomElementData* aData)
{
nsDOMSlots *slots = DOMSlots();
MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
slots->mCustomElementData = aData;
}
nsresult nsresult
FragmentOrElement::InsertChildAt(nsIContent* aKid, FragmentOrElement::InsertChildAt(nsIContent* aKid,
uint32_t aIndex, uint32_t aIndex,
@ -1366,14 +1380,15 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
{ {
nsDOMSlots *slots = tmp->GetExistingDOMSlots(); nsDOMSlots *slots = tmp->GetExistingDOMSlots();
if (slots) { if (slots) {
if (tmp->IsElement()) { if (slots->mExtendedSlots && tmp->IsElement()) {
Element* elem = tmp->AsElement(); Element* elem = tmp->AsElement();
for (auto iter = slots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { for (auto iter = slots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
!iter.Done(); iter.Next()) {
DOMIntersectionObserver* observer = iter.Key(); DOMIntersectionObserver* observer = iter.Key();
observer->UnlinkTarget(*elem); observer->UnlinkTarget(*elem);
} }
} }
slots->Unlink(tmp->IsXULElement()); slots->Unlink();
} }
} }
@ -1938,7 +1953,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
{ {
nsDOMSlots *slots = tmp->GetExistingDOMSlots(); nsDOMSlots *slots = tmp->GetExistingDOMSlots();
if (slots) { if (slots) {
slots->Traverse(cb, tmp->IsXULElement()); slots->Traverse(cb);
} }
} }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

View File

@ -15,6 +15,7 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
#include "nsAttrAndChildArray.h" // member #include "nsAttrAndChildArray.h" // member
#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_* #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
#include "nsIContent.h" // base class #include "nsIContent.h" // base class
@ -37,6 +38,7 @@ class nsIURI;
namespace mozilla { namespace mozilla {
class DeclarationBlock; class DeclarationBlock;
namespace dom { namespace dom {
struct CustomElementData;
class DOMIntersectionObserver; class DOMIntersectionObserver;
class Element; class Element;
} // namespace dom } // namespace dom
@ -159,9 +161,6 @@ public:
virtual void SetXBLInsertionParent(nsIContent* aContent) override; virtual void SetXBLInsertionParent(nsIContent* aContent) override;
virtual bool IsLink(nsIURI** aURI) const override; virtual bool IsLink(nsIURI** aURI) const override;
virtual CustomElementData *GetCustomElementData() const override;
virtual void SetCustomElementData(CustomElementData* aData) override;
virtual void DestroyContent() override; virtual void DestroyContent() override;
virtual void SaveSubtreeState() override; virtual void SaveSubtreeState() override;
@ -241,8 +240,6 @@ protected:
nsresult CopyInnerTo(FragmentOrElement* aDest); nsresult CopyInnerTo(FragmentOrElement* aDest);
public: public:
// Because of a bug in MS C++ compiler nsDOMSlots must be declared public,
// otherwise nsXULElement::nsXULSlots doesn't compile.
/** /**
* There are a set of DOM- and scripting-specific instance variables * There are a set of DOM- and scripting-specific instance variables
* that may only be instantiated when a content object is accessed * that may only be instantiated when a content object is accessed
@ -251,29 +248,13 @@ public:
* in a side structure that's only allocated when the content is * in a side structure that's only allocated when the content is
* accessed through the DOM. * accessed through the DOM.
*/ */
class nsDOMSlots : public nsINode::nsSlots
class nsExtendedDOMSlots
{ {
public: public:
nsDOMSlots(); nsExtendedDOMSlots();
virtual ~nsDOMSlots();
void Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL); ~nsExtendedDOMSlots();
void Unlink(bool aIsXUL);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
/**
* The .style attribute (an interface that forwards to the actual
* style rules)
* @see nsGenericHTMLElement::GetStyle
*/
nsCOMPtr<nsICSSDeclaration> mStyle;
/**
* The .dataset attribute.
* @see nsGenericHTMLElement::GetDataset
*/
nsDOMStringMap* mDataset; // [Weak]
/** /**
* SMIL Overridde style rules (for SMIL animation of CSS properties) * SMIL Overridde style rules (for SMIL animation of CSS properties)
@ -287,35 +268,17 @@ public:
RefPtr<mozilla::DeclarationBlock> mSMILOverrideStyleDeclaration; RefPtr<mozilla::DeclarationBlock> mSMILOverrideStyleDeclaration;
/** /**
* An object implementing nsIDOMMozNamedAttrMap for this content (attributes) * The nearest enclosing content node with a binding that created us.
* @see FragmentOrElement::GetAttributes * @see FragmentOrElement::GetBindingParent
*/ */
RefPtr<nsDOMAttributeMap> mAttributeMap; nsIContent* mBindingParent; // [Weak]
union {
/**
* The nearest enclosing content node with a binding that created us.
* @see FragmentOrElement::GetBindingParent
*/
nsIContent* mBindingParent; // [Weak]
/**
* The controllers of the XUL Element.
*/
nsIControllers* mControllers; // [OWNER]
};
/** /**
* An object implementing the .children property for this element. * The controllers of the XUL Element.
*/ */
RefPtr<nsContentList> mChildrenList; nsCOMPtr<nsIControllers> mControllers;
/** /**
* An object implementing the .classList property for this element.
*/
RefPtr<nsDOMTokenList> mClassList;
/*
* An object implementing the .labels property for this element. * An object implementing the .labels property for this element.
*/ */
RefPtr<nsLabelsNodeList> mLabelsList; RefPtr<nsLabelsNodeList> mLabelsList;
@ -356,6 +319,55 @@ public:
*/ */
nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t> nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
mRegisteredIntersectionObservers; mRegisteredIntersectionObservers;
/**
* For XUL to hold either frameloader or opener.
*/
nsCOMPtr<nsISupports> mFrameLoaderOrOpener;
};
class nsDOMSlots : public nsINode::nsSlots
{
public:
nsDOMSlots();
virtual ~nsDOMSlots();
void Traverse(nsCycleCollectionTraversalCallback &cb);
void Unlink();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
/**
* The .style attribute (an interface that forwards to the actual
* style rules)
* @see nsGenericHTMLElement::GetStyle
*/
nsCOMPtr<nsICSSDeclaration> mStyle;
/**
* The .dataset attribute.
* @see nsGenericHTMLElement::GetDataset
*/
nsDOMStringMap* mDataset; // [Weak]
/**
* An object implementing nsIDOMMozNamedAttrMap for this content (attributes)
* @see FragmentOrElement::GetAttributes
*/
RefPtr<nsDOMAttributeMap> mAttributeMap;
/**
* An object implementing the .children property for this element.
*/
RefPtr<nsContentList> mChildrenList;
/**
* An object implementing the .classList property for this element.
*/
RefPtr<nsDOMTokenList> mClassList;
mozilla::UniquePtr<nsExtendedDOMSlots> mExtendedSlots;
}; };
protected: protected:
@ -375,6 +387,26 @@ protected:
return static_cast<nsDOMSlots*>(GetExistingSlots()); return static_cast<nsDOMSlots*>(GetExistingSlots());
} }
nsExtendedDOMSlots* ExtendedDOMSlots()
{
nsDOMSlots* slots = DOMSlots();
if (!slots->mExtendedSlots) {
slots->mExtendedSlots = MakeUnique<nsExtendedDOMSlots>();
}
return slots->mExtendedSlots.get();
}
nsExtendedDOMSlots* GetExistingExtendedDOMSlots() const
{
nsDOMSlots* slots = GetExistingDOMSlots();
if (slots) {
return slots->mExtendedSlots.get();
}
return nullptr;
}
/** /**
* Calls SetIsElementInStyleScopeFlagOnSubtree for each shadow tree attached * Calls SetIsElementInStyleScopeFlagOnSubtree for each shadow tree attached
* to this node, which is assumed to be an Element. * to this node, which is assumed to be an Element.

View File

@ -75,8 +75,8 @@ ShadowRoot::ShadowRoot(nsIContent* aContent,
SetFlags(NODE_IS_IN_SHADOW_TREE); SetFlags(NODE_IS_IN_SHADOW_TREE);
DOMSlots()->mBindingParent = aContent; ExtendedDOMSlots()->mBindingParent = aContent;
DOMSlots()->mContainingShadow = this; ExtendedDOMSlots()->mContainingShadow = this;
// Add the ShadowRoot as a mutation observer on the host to watch // Add the ShadowRoot as a mutation observer on the host to watch
// for mutations because the insertion points in this ShadowRoot // for mutations because the insertion points in this ShadowRoot

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body>
<script>
var o1 = document.documentElement;
var o2 = document.createElement("frame");
document.documentElement.appendChild(o2);
var o3 = o2.contentWindow;
o1.parentNode.removeChild(o1);
o3.customElements;
</script>
</body>
</html>

View File

@ -209,3 +209,4 @@ load 1230422.html
load 1251361.html load 1251361.html
load 1304437.html load 1304437.html
pref(clipboard.autocopy,true) load 1385272-1.html pref(clipboard.autocopy,true) load 1385272-1.html
pref(dom.webcomponents.customelements.enabled,true) load 1341693.html

View File

@ -24,6 +24,7 @@ namespace mozilla {
namespace dom { namespace dom {
class Element; class Element;
class NodeInfo; class NodeInfo;
struct CustomElementDefinition;
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla
@ -41,7 +42,8 @@ nsresult
NS_NewHTMLElement(mozilla::dom::Element** aResult, NS_NewHTMLElement(mozilla::dom::Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser, mozilla::dom::FromParser aFromParser,
const nsAString* aIs = nullptr); const nsAString* aIs = nullptr,
mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
// First argument should be nsHTMLTag, but that adds dependency to parser // First argument should be nsHTMLTag, but that adds dependency to parser
// for a bunch of files. // for a bunch of files.

View File

@ -227,6 +227,7 @@ extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
int ns_aware, const char** colon); int ns_aware, const char** colon);
class imgLoader; class imgLoader;
class nsIAtom;
using namespace mozilla::dom; using namespace mozilla::dom;
using namespace mozilla::ipc; using namespace mozilla::ipc;
@ -258,7 +259,6 @@ nsIWordBreaker *nsContentUtils::sWordBreaker;
nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr; nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nullptr;
uint32_t nsContentUtils::sScriptBlockerCount = 0; uint32_t nsContentUtils::sScriptBlockerCount = 0;
uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0; uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
uint32_t nsContentUtils::sMicroTaskLevel = 0;
AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr; AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners = nullptr;
uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0; uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr; nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
@ -284,6 +284,8 @@ bool nsContentUtils::sIsResourceTimingEnabled = false;
bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false; bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false;
bool nsContentUtils::sIsUserTimingLoggingEnabled = false; bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false; bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
bool nsContentUtils::sIsWebComponentsEnabled = false;
bool nsContentUtils::sIsCustomElementsEnabled = false;
bool nsContentUtils::sEncodeDecodeURLHash = false; bool nsContentUtils::sEncodeDecodeURLHash = false;
bool nsContentUtils::sGettersDecodeURLHash = false; bool nsContentUtils::sGettersDecodeURLHash = false;
bool nsContentUtils::sPrivacyResistFingerprinting = false; bool nsContentUtils::sPrivacyResistFingerprinting = false;
@ -584,6 +586,12 @@ nsContentUtils::Init()
Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled, Preferences::AddBoolVarCache(&sIsExperimentalAutocompleteEnabled,
"dom.forms.autocomplete.experimental", false); "dom.forms.autocomplete.experimental", false);
Preferences::AddBoolVarCache(&sIsWebComponentsEnabled,
"dom.webcomponents.enabled", false);
Preferences::AddBoolVarCache(&sIsCustomElementsEnabled,
"dom.webcomponents.customelements.enabled", false);
Preferences::AddBoolVarCache(&sEncodeDecodeURLHash, Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
"dom.url.encode_decode_hash", false); "dom.url.encode_decode_hash", false);
@ -5293,51 +5301,6 @@ nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable)); CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable));
} }
void
nsContentUtils::EnterMicroTask()
{
MOZ_ASSERT(NS_IsMainThread());
++sMicroTaskLevel;
}
void
nsContentUtils::LeaveMicroTask()
{
MOZ_ASSERT(NS_IsMainThread());
if (--sMicroTaskLevel == 0) {
PerformMainThreadMicroTaskCheckpoint();
}
}
bool
nsContentUtils::IsInMicroTask()
{
MOZ_ASSERT(NS_IsMainThread());
return sMicroTaskLevel != 0;
}
uint32_t
nsContentUtils::MicroTaskLevel()
{
MOZ_ASSERT(NS_IsMainThread());
return sMicroTaskLevel;
}
void
nsContentUtils::SetMicroTaskLevel(uint32_t aLevel)
{
MOZ_ASSERT(NS_IsMainThread());
sMicroTaskLevel = aLevel;
}
void
nsContentUtils::PerformMainThreadMicroTaskCheckpoint()
{
MOZ_ASSERT(NS_IsMainThread());
nsDOMMutationObserver::HandleMutations();
}
/* /*
* Helper function for nsContentUtils::ProcessViewportInfo. * Helper function for nsContentUtils::ProcessViewportInfo.
* *
@ -9567,11 +9530,34 @@ nsContentUtils::HttpsStateIsModern(nsIDocument* aDocument)
return false; return false;
} }
/* static */ void
nsContentUtils::TryToUpgradeElement(Element* aElement)
{
NodeInfo* nodeInfo = aElement->NodeInfo();
RefPtr<nsIAtom> typeAtom =
aElement->GetCustomElementData()->GetCustomElementType();
MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
CustomElementDefinition* definition =
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
nodeInfo->NameAtom(),
nodeInfo->NamespaceID(),
typeAtom);
if (definition) {
nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
} else {
// Add an unresolved custom element that is a candidate for
// upgrade when a custom element is connected to the document.
// We will make sure it's shadow-including tree order in bug 1326028.
nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
}
}
/* static */ CustomElementDefinition* /* static */ CustomElementDefinition*
nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc, nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc,
const nsAString& aLocalName, nsIAtom* aNameAtom,
uint32_t aNameSpaceID, uint32_t aNameSpaceID,
const nsAString* aIs) nsIAtom* aTypeAtom)
{ {
MOZ_ASSERT(aDoc); MOZ_ASSERT(aDoc);
@ -9593,30 +9579,16 @@ nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc,
return nullptr; return nullptr;
} }
return registry->LookupCustomElementDefinition(aLocalName, aIs); return registry->LookupCustomElementDefinition(aNameAtom, aTypeAtom);
} }
/* static */ void /* static */ void
nsContentUtils::SetupCustomElement(Element* aElement, nsContentUtils::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
const nsAString* aTypeExtension)
{ {
MOZ_ASSERT(aElement); MOZ_ASSERT(aElement);
nsCOMPtr<nsIDocument> doc = aElement->OwnerDoc(); nsIDocument* doc = aElement->OwnerDoc();
nsPIDOMWindowInner* window(doc->GetInnerWindow());
if (!doc) {
return;
}
// To support imported document.
doc = doc->MasterDocument();
if (aElement->GetNameSpaceID() != kNameSpaceID_XHTML ||
!doc->GetDocShell()) {
return;
}
nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
if (!window) { if (!window) {
return; return;
} }
@ -9626,26 +9598,18 @@ nsContentUtils::SetupCustomElement(Element* aElement,
return; return;
} }
return registry->SetupCustomElement(aElement, aTypeExtension); registry->RegisterUnresolvedElement(aElement, aTypeName);
} }
/* static */ void /* static */ void
nsContentUtils::EnqueueLifecycleCallback(nsIDocument* aDoc, nsContentUtils::UnregisterUnresolvedElement(Element* aElement)
nsIDocument::ElementCallbackType aType,
Element* aCustomElement,
LifecycleCallbackArgs* aArgs,
CustomElementDefinition* aDefinition)
{ {
MOZ_ASSERT(aDoc); MOZ_ASSERT(aElement);
// To support imported document. RefPtr<nsIAtom> typeAtom =
nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument(); aElement->GetCustomElementData()->GetCustomElementType();
nsIDocument* doc = aElement->OwnerDoc();
if (!doc->GetDocShell()) { nsPIDOMWindowInner* window(doc->GetInnerWindow());
return;
}
nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
if (!window) { if (!window) {
return; return;
} }
@ -9655,7 +9619,59 @@ nsContentUtils::EnqueueLifecycleCallback(nsIDocument* aDoc,
return; return;
} }
registry->EnqueueLifecycleCallback(aType, aCustomElement, aArgs, aDefinition); registry->UnregisterUnresolvedElement(aElement, typeAtom);
}
/* static */ CustomElementDefinition*
nsContentUtils::GetElementDefinitionIfObservingAttr(Element* aCustomElement,
nsIAtom* aExtensionType,
nsIAtom* aAttrName)
{
CustomElementDefinition* definition =
aCustomElement->GetCustomElementDefinition();
// Custom element not defined yet or attribute is not in the observed
// attribute list.
if (!definition || !definition->IsInObservedAttributeList(aAttrName)) {
return nullptr;
}
return definition;
}
/* static */ void
nsContentUtils::EnqueueUpgradeReaction(Element* aElement,
CustomElementDefinition* aDefinition)
{
MOZ_ASSERT(aElement);
nsIDocument* doc = aElement->OwnerDoc();
// No DocGroup means no custom element reactions stack.
if (!doc->GetDocGroup()) {
return;
}
CustomElementReactionsStack* stack =
doc->GetDocGroup()->CustomElementReactionsStack();
stack->EnqueueUpgradeReaction(aElement, aDefinition);
}
/* static */ void
nsContentUtils::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
Element* aCustomElement,
LifecycleCallbackArgs* aArgs,
LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
CustomElementDefinition* aDefinition)
{
// No DocGroup means no custom element reactions stack.
if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
return;
}
CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
aAdoptedCallbackArgs,
aDefinition);
} }
/* static */ void /* static */ void
@ -9834,4 +9850,4 @@ nsContentUtils::IsLocalRefURL(const nsString& aString)
} }
return false; return false;
} }

View File

@ -126,6 +126,7 @@ class EventTarget;
class IPCDataTransfer; class IPCDataTransfer;
class IPCDataTransferItem; class IPCDataTransferItem;
struct LifecycleCallbackArgs; struct LifecycleCallbackArgs;
struct LifecycleAdoptedCallbackArgs;
class NodeInfo; class NodeInfo;
class nsIContentChild; class nsIContentChild;
class nsIContentParent; class nsIContentParent;
@ -1739,17 +1740,6 @@ public:
*/ */
static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable); static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable);
// Call EnterMicroTask when you're entering JS execution.
// Usually the best way to do this is to use nsAutoMicroTask.
static void EnterMicroTask();
static void LeaveMicroTask();
static bool IsInMicroTask();
static uint32_t MicroTaskLevel();
static void SetMicroTaskLevel(uint32_t aLevel);
static void PerformMainThreadMicroTaskCheckpoint();
/* Process viewport META data. This gives us information for the scale /* Process viewport META data. This gives us information for the scale
* and zoom of a page on mobile devices. We stick the information in * and zoom of a page on mobile devices. We stick the information in
* the document header and use it later on after rendering. * the document header and use it later on after rendering.
@ -2710,23 +2700,37 @@ public:
*/ */
static bool HttpsStateIsModern(nsIDocument* aDocument); static bool HttpsStateIsModern(nsIDocument* aDocument);
/**
* Try to upgrade an element.
* https://html.spec.whatwg.org/multipage/custom-elements.html#concept-try-upgrade
*/
static void TryToUpgradeElement(Element* aElement);
/** /**
* Looking up a custom element definition. * Looking up a custom element definition.
* https://html.spec.whatwg.org/#look-up-a-custom-element-definition * https://html.spec.whatwg.org/#look-up-a-custom-element-definition
*/ */
static mozilla::dom::CustomElementDefinition* static mozilla::dom::CustomElementDefinition*
LookupCustomElementDefinition(nsIDocument* aDoc, LookupCustomElementDefinition(nsIDocument* aDoc,
const nsAString& aLocalName, nsIAtom* aNameAtom,
uint32_t aNameSpaceID, uint32_t aNameSpaceID,
const nsAString* aIs = nullptr); nsIAtom* aTypeAtom);
static void SetupCustomElement(Element* aElement, static void RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName);
const nsAString* aTypeExtension = nullptr); static void UnregisterUnresolvedElement(Element* aElement);
static void EnqueueLifecycleCallback(nsIDocument* aDoc, static mozilla::dom::CustomElementDefinition*
nsIDocument::ElementCallbackType aType, GetElementDefinitionIfObservingAttr(Element* aCustomElement,
nsIAtom* aExtensionType,
nsIAtom* aAttrName);
static void EnqueueUpgradeReaction(Element* aElement,
mozilla::dom::CustomElementDefinition* aDefinition);
static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
Element* aCustomElement, Element* aCustomElement,
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr, mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
mozilla::dom::LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs = nullptr,
mozilla::dom::CustomElementDefinition* aDefinition = nullptr); mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
static void GetCustomPrototype(nsIDocument* aDoc, static void GetCustomPrototype(nsIDocument* aDoc,
@ -2743,6 +2747,12 @@ public:
static bool static bool
IsLocalRefURL(const nsString& aString); IsLocalRefURL(const nsString& aString);
static bool
IsWebComponentsEnabled() { return sIsWebComponentsEnabled; }
static bool
IsCustomElementsEnabled() { return sIsCustomElementsEnabled; }
private: private:
static bool InitializeEventTable(); static bool InitializeEventTable();
@ -2829,7 +2839,6 @@ private:
static bool sInitialized; static bool sInitialized;
static uint32_t sScriptBlockerCount; static uint32_t sScriptBlockerCount;
static uint32_t sDOMNodeRemovedSuppressCount; static uint32_t sDOMNodeRemovedSuppressCount;
static uint32_t sMicroTaskLevel;
// Not an nsCOMArray because removing elements from those is slower // Not an nsCOMArray because removing elements from those is slower
static AutoTArray<nsCOMPtr<nsIRunnable>, 8>* sBlockedScriptRunners; static AutoTArray<nsCOMPtr<nsIRunnable>, 8>* sBlockedScriptRunners;
static uint32_t sRunnersCountAtFirstBlocker; static uint32_t sRunnersCountAtFirstBlocker;
@ -2850,6 +2859,8 @@ private:
static bool sIsUserTimingLoggingEnabled; static bool sIsUserTimingLoggingEnabled;
static bool sIsFrameTimingPrefEnabled; static bool sIsFrameTimingPrefEnabled;
static bool sIsExperimentalAutocompleteEnabled; static bool sIsExperimentalAutocompleteEnabled;
static bool sIsWebComponentsEnabled;
static bool sIsCustomElementsEnabled;
static bool sEncodeDecodeURLHash; static bool sEncodeDecodeURLHash;
static bool sGettersDecodeURLHash; static bool sGettersDecodeURLHash;
static bool sPrivacyResistFingerprinting; static bool sPrivacyResistFingerprinting;
@ -2905,19 +2916,6 @@ public:
} }
}; };
class MOZ_STACK_CLASS nsAutoMicroTask
{
public:
nsAutoMicroTask()
{
nsContentUtils::EnterMicroTask();
}
~nsAutoMicroTask()
{
nsContentUtils::LeaveMicroTask();
}
};
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {

View File

@ -32,8 +32,6 @@ using mozilla::dom::Element;
AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* AutoTArray<RefPtr<nsDOMMutationObserver>, 4>*
nsDOMMutationObserver::sScheduledMutationObservers = nullptr; nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
uint32_t nsDOMMutationObserver::sMutationLevel = 0; uint32_t nsDOMMutationObserver::sMutationLevel = 0;
uint64_t nsDOMMutationObserver::sCount = 0; uint64_t nsDOMMutationObserver::sCount = 0;
@ -597,10 +595,32 @@ nsDOMMutationObserver::ScheduleForRun()
RescheduleForRun(); RescheduleForRun();
} }
class MutationObserverMicroTask final : public MicroTaskRunnable
{
public:
virtual void Run(AutoSlowOperation& aAso) override
{
nsDOMMutationObserver::HandleMutations(aAso);
}
virtual bool Suppressed() override
{
return nsDOMMutationObserver::AllScheduledMutationObserversAreSuppressed();
}
};
void void
nsDOMMutationObserver::RescheduleForRun() nsDOMMutationObserver::RescheduleForRun()
{ {
if (!sScheduledMutationObservers) { if (!sScheduledMutationObservers) {
CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (!ccjs) {
return;
}
RefPtr<MutationObserverMicroTask> momt =
new MutationObserverMicroTask();
ccjs->DispatchMicroTaskRunnable(momt.forget());
sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>; sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>;
} }
@ -862,36 +882,9 @@ nsDOMMutationObserver::HandleMutation()
mCallback->Call(this, mutations, *this); mCallback->Call(this, mutations, *this);
} }
class AsyncMutationHandler : public mozilla::Runnable
{
public:
NS_IMETHOD Run() override
{
nsDOMMutationObserver::HandleMutations();
return NS_OK;
}
};
void void
nsDOMMutationObserver::HandleMutationsInternal() nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso)
{ {
if (!nsContentUtils::IsSafeToRunScript()) {
nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
return;
}
static RefPtr<nsDOMMutationObserver> sCurrentObserver;
if (sCurrentObserver && !sCurrentObserver->Suppressed()) {
// In normal cases sScheduledMutationObservers will be handled
// after previous mutations are handled. But in case some
// callback calls a sync API, which spins the eventloop, we need to still
// process other mutations happening during that sync call.
// This does *not* catch all cases, but should work for stuff running
// in separate tabs.
return;
}
mozilla::AutoSlowOperation aso;
nsTArray<RefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr; nsTArray<RefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
while (sScheduledMutationObservers) { while (sScheduledMutationObservers) {
@ -899,20 +892,21 @@ nsDOMMutationObserver::HandleMutationsInternal()
sScheduledMutationObservers; sScheduledMutationObservers;
sScheduledMutationObservers = nullptr; sScheduledMutationObservers = nullptr;
for (uint32_t i = 0; i < observers->Length(); ++i) { for (uint32_t i = 0; i < observers->Length(); ++i) {
sCurrentObserver = static_cast<nsDOMMutationObserver*>((*observers)[i]); RefPtr<nsDOMMutationObserver> currentObserver =
if (!sCurrentObserver->Suppressed()) { static_cast<nsDOMMutationObserver*>((*observers)[i]);
sCurrentObserver->HandleMutation(); if (!currentObserver->Suppressed()) {
currentObserver->HandleMutation();
} else { } else {
if (!suppressedObservers) { if (!suppressedObservers) {
suppressedObservers = new nsTArray<RefPtr<nsDOMMutationObserver> >; suppressedObservers = new nsTArray<RefPtr<nsDOMMutationObserver> >;
} }
if (!suppressedObservers->Contains(sCurrentObserver)) { if (!suppressedObservers->Contains(currentObserver)) {
suppressedObservers->AppendElement(sCurrentObserver); suppressedObservers->AppendElement(currentObserver);
} }
} }
} }
delete observers; delete observers;
aso.CheckForInterrupt(); aAso.CheckForInterrupt();
} }
if (suppressedObservers) { if (suppressedObservers) {
@ -923,7 +917,6 @@ nsDOMMutationObserver::HandleMutationsInternal()
delete suppressedObservers; delete suppressedObservers;
suppressedObservers = nullptr; suppressedObservers = nullptr;
} }
sCurrentObserver = nullptr;
} }
nsDOMMutationRecord* nsDOMMutationRecord*

View File

@ -552,13 +552,29 @@ public:
} }
// static methods // static methods
static void HandleMutations() static void HandleMutations(mozilla::AutoSlowOperation& aAso)
{ {
if (sScheduledMutationObservers) { if (sScheduledMutationObservers) {
HandleMutationsInternal(); HandleMutationsInternal(aAso);
} }
} }
static bool AllScheduledMutationObserversAreSuppressed()
{
if (sScheduledMutationObservers) {
uint32_t len = sScheduledMutationObservers->Length();
if (len > 0) {
for (uint32_t i = 0; i < len; ++i) {
if (!(*sScheduledMutationObservers)[i]->Suppressed()) {
return false;
}
}
return true;
}
}
return false;
}
static void EnterMutationHandling(); static void EnterMutationHandling();
static void LeaveMutationHandling(); static void LeaveMutationHandling();
@ -594,7 +610,7 @@ protected:
return false; return false;
} }
static void HandleMutationsInternal(); static void HandleMutationsInternal(mozilla::AutoSlowOperation& aAso);
static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver, static void AddCurrentlyHandlingObserver(nsDOMMutationObserver* aObserver,
uint32_t aMutationLevel); uint32_t aMutationLevel);
@ -622,7 +638,6 @@ protected:
static uint64_t sCount; static uint64_t sCount;
static AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers; static AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* sScheduledMutationObservers;
static nsDOMMutationObserver* sCurrentObserver;
static uint32_t sMutationLevel; static uint32_t sMutationLevel;
static AutoTArray<AutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>* static AutoTArray<AutoTArray<RefPtr<nsDOMMutationObserver>, 4>, 4>*

View File

@ -1329,7 +1329,8 @@ nsIDocument::nsIDocument()
mFrameRequestCallbacksScheduled(false), mFrameRequestCallbacksScheduled(false),
mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS), mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
mPartID(0), mPartID(0),
mUserHasInteracted(false) mUserHasInteracted(false),
mThrowOnDynamicMarkupInsertionCounter(0)
{ {
SetIsInDocument(); SetIsInDocument();
@ -5395,18 +5396,14 @@ nsDocument::CreateElement(const nsAString& aTagName,
} }
const nsString* is = nullptr; const nsString* is = nullptr;
if (aOptions.IsElementCreationOptions()) {
// Throw NotFoundError if 'is' is not-null and definition is null
is = CheckCustomElementName(aOptions.GetAsElementCreationOptions(),
needsLowercase ? lcTagName : aTagName, mDefaultElementType, rv);
if (rv.Failed()) {
return nullptr;
}
}
RefPtr<Element> elem = CreateElem( RefPtr<Element> elem = CreateElem(
needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is); needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
if (is) {
elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
}
return elem.forget(); return elem.forget();
} }
@ -5443,14 +5440,6 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
} }
const nsString* is = nullptr; const nsString* is = nullptr;
if (aOptions.IsElementCreationOptions()) {
// Throw NotFoundError if 'is' is not-null and definition is null
is = CheckCustomElementName(aOptions.GetAsElementCreationOptions(),
aQualifiedName, nodeInfo->NamespaceID(), rv);
if (rv.Failed()) {
return nullptr;
}
}
nsCOMPtr<Element> element; nsCOMPtr<Element> element;
rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
@ -5459,6 +5448,10 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
return nullptr; return nullptr;
} }
if (is) {
element->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
}
return element.forget(); return element.forget();
} }
@ -5681,24 +5674,70 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
} }
nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName)); nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
CustomElementDefinition* definition = registry->mCustomDefinitions.Get(typeAtom); CustomElementDefinition* definition =
registry->mCustomDefinitions.GetWeak(typeAtom);
if (!definition) { if (!definition) {
return true; return true;
} }
nsDependentAtomString localName(definition->mLocalName); RefPtr<Element> element;
nsCOMPtr<Element> element = // We integrate with construction stack and do prototype swizzling here, so
document->CreateElem(localName, nullptr, kNameSpaceID_XHTML); // that old upgrade behavior could also share the new upgrade steps.
NS_ENSURE_TRUE(element, true); // And this old upgrade will be remove at some point (when everything is
// switched to latest custom element spec).
nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack =
definition->mConstructionStack;
if (constructionStack.Length()) {
element = constructionStack.LastElement();
NS_ENSURE_TRUE(element != ALEADY_CONSTRUCTED_MARKER, false);
if (definition->mLocalName != typeAtom) { // Do prototype swizzling if dom reflector exists.
// This element is a custom element by extension, thus we need to JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper());
// do some special setup. For non-extended custom elements, this happens if (reflector) {
// when the element is created. Maybe<JSAutoCompartment> ac;
nsContentUtils::SetupCustomElement(element, &elemName); JS::Rooted<JSObject*> prototype(aCx, definition->mPrototype);
if (element->NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(prototype))) {
ac.emplace(aCx, reflector);
if (!JS_WrapObject(aCx, &prototype) ||
!JS_SetPrototype(aCx, reflector, prototype)) {
return false;
}
} else {
// We want to set the custom prototype in the compartment where it was
// registered. We store the prototype from define() without unwrapped,
// hence the prototype's compartment is the compartment where it was
// registered.
// In the case that |reflector| and |prototype| are in different
// compartments, this will set the prototype on the |reflector|'s wrapper
// and thus only visible in the wrapper's compartment, since we know
// reflector's principal does not subsume prototype's in this case.
ac.emplace(aCx, prototype);
if (!JS_WrapObject(aCx, &reflector) ||
!JS_SetPrototype(aCx, reflector, prototype)) {
return false;
}
}
// Wrap into current context.
if (!JS_WrapObject(aCx, &reflector)) {
return false;
}
args.rval().setObject(*reflector);
return true;
}
} else {
nsDependentAtomString localName(definition->mLocalName);
element =
document->CreateElem(localName, nullptr, kNameSpaceID_XHTML,
(definition->mLocalName != typeAtom) ? &elemName
: nullptr);
NS_ENSURE_TRUE(element, false);
} }
// The prototype setup happens in Element::WrapObject().
nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval()); nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval());
NS_ENSURE_SUCCESS(rv, true); NS_ENSURE_SUCCESS(rv, true);
@ -5710,7 +5749,7 @@ nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
{ {
JS::Rooted<JSObject*> obj(aCx, aObject); JS::Rooted<JSObject*> obj(aCx, aObject);
if (Preferences::GetBool("dom.webcomponents.enabled")) { if (nsContentUtils::IsWebComponentsEnabled()) {
return true; return true;
} }
@ -5726,7 +5765,7 @@ nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
bool bool
nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo) nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo)
{ {
if (Preferences::GetBool("dom.webcomponents.enabled")) { if (nsContentUtils::IsWebComponentsEnabled()) {
return true; return true;
} }
@ -5770,6 +5809,8 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
return; return;
} }
AutoCEReaction ceReaction(this->GetDocGroup()->CustomElementReactionsStack(),
aCx);
// Unconditionally convert TYPE to lowercase. // Unconditionally convert TYPE to lowercase.
nsAutoString lcType; nsAutoString lcType;
nsContentUtils::ASCIIToLower(aType, lcType); nsContentUtils::ASCIIToLower(aType, lcType);
@ -6536,6 +6577,49 @@ nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
return nullptr; return nullptr;
} }
nsGenericHTMLElement*
nsIDocument::GetBody()
{
Element* html = GetHtmlElement();
if (!html) {
return nullptr;
}
for (nsIContent* child = html->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (child->IsHTMLElement(nsGkAtoms::body) ||
child->IsHTMLElement(nsGkAtoms::frameset)) {
return static_cast<nsGenericHTMLElement*>(child);
}
}
return nullptr;
}
void
nsIDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv)
{
nsCOMPtr<Element> root = GetRootElement();
// The body element must be either a body tag or a frameset tag. And we must
// have a root element to be able to add kids to it.
if (!newBody ||
!newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset) ||
!root) {
rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return;
}
// Use DOM methods so that we pass through the appropriate security checks.
nsCOMPtr<Element> currentBody = GetBody();
if (currentBody) {
root->ReplaceChild(*newBody, *currentBody, rv);
} else {
root->AppendChild(*newBody, rv);
}
}
Element* Element*
nsDocument::GetTitleElement() nsDocument::GetTitleElement()
{ {
@ -12526,8 +12610,12 @@ MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc) nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
{ {
mMicroTaskLevel = nsContentUtils::MicroTaskLevel(); mMicroTaskLevel = 0;
nsContentUtils::SetMicroTaskLevel(0); CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
mMicroTaskLevel = ccjs->MicroTaskLevel();
ccjs->SetMicroTaskLevel(0);
}
if (aDoc) { if (aDoc) {
if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) { if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) { if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
@ -12543,7 +12631,10 @@ nsAutoSyncOperation::~nsAutoSyncOperation()
for (int32_t i = 0; i < mDocuments.Count(); ++i) { for (int32_t i = 0; i < mDocuments.Count(); ++i) {
mDocuments[i]->SetIsInSyncOperation(false); mDocuments[i]->SetIsInSyncOperation(false);
} }
nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel); CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
ccjs->SetMicroTaskLevel(mMicroTaskLevel);
}
} }
gfxUserFontSet* gfxUserFontSet*
@ -12713,30 +12804,6 @@ nsIDocument::UpdateStyleBackendType()
#endif #endif
} }
const nsString*
nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
const nsAString& aLocalName,
uint32_t aNamespaceID,
ErrorResult& rv)
{
// only check aOptions if 'is' is passed and the webcomponents preference
// is enabled
if (!aOptions.mIs.WasPassed() ||
!CustomElementRegistry::IsCustomElementEnabled()) {
return nullptr;
}
const nsString* is = &aOptions.mIs.Value();
// Throw NotFoundError if 'is' is not-null and definition is null
if (!nsContentUtils::LookupCustomElementDefinition(this, aLocalName,
aNamespaceID, is)) {
rv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
}
return is;
}
Selection* Selection*
nsIDocument::GetSelection(ErrorResult& aRv) nsIDocument::GetSelection(ErrorResult& aRv)
{ {

View File

@ -1388,20 +1388,6 @@ protected:
private: private:
static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp); static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
/**
* Check if the passed custom element name, aOptions.mIs, is a registered
* custom element type or not, then return the custom element name for future
* usage.
*
* If there is no existing custom element definition for this name, throw a
* NotFoundError.
*/
const nsString* CheckCustomElementName(
const mozilla::dom::ElementCreationOptions& aOptions,
const nsAString& aLocalName,
uint32_t aNamespaceID,
ErrorResult& rv);
public: public:
virtual already_AddRefed<mozilla::dom::CustomElementRegistry> virtual already_AddRefed<mozilla::dom::CustomElementRegistry>
GetCustomElementRegistry() override; GetCustomElementRegistry() override;

View File

@ -793,17 +793,6 @@ nsGenericDOMDataNode::SetXBLInsertionParent(nsIContent* aContent)
} }
} }
CustomElementData *
nsGenericDOMDataNode::GetCustomElementData() const
{
return nullptr;
}
void
nsGenericDOMDataNode::SetCustomElementData(CustomElementData* aData)
{
}
bool bool
nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const
{ {

View File

@ -162,9 +162,6 @@ public:
virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual bool IsNodeOfType(uint32_t aFlags) const override;
virtual bool IsLink(nsIURI** aURI) const override; virtual bool IsLink(nsIURI** aURI) const override;
virtual mozilla::dom::CustomElementData* GetCustomElementData() const override;
virtual void SetCustomElementData(mozilla::dom::CustomElementData* aData) override;
NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override;
NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const; NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,

View File

@ -4241,8 +4241,9 @@ CustomElementRegistry*
nsGlobalWindow::CustomElements() nsGlobalWindow::CustomElements()
{ {
MOZ_RELEASE_ASSERT(IsInnerWindow()); MOZ_RELEASE_ASSERT(IsInnerWindow());
if (!mCustomElements) { if (!mCustomElements) {
mCustomElements = CustomElementRegistry::Create(AsInner()); mCustomElements = new CustomElementRegistry(AsInner());
} }
return mCustomElements; return mCustomElements;

View File

@ -26,7 +26,6 @@ namespace mozilla {
class EventChainPreVisitor; class EventChainPreVisitor;
namespace dom { namespace dom {
class ShadowRoot; class ShadowRoot;
struct CustomElementData;
} // namespace dom } // namespace dom
namespace widget { namespace widget {
struct IMEState; struct IMEState;
@ -729,22 +728,6 @@ public:
*/ */
nsINode *GetFlattenedTreeParentNodeInternal() const; nsINode *GetFlattenedTreeParentNodeInternal() const;
/**
* Gets the custom element data used by web components custom element.
* Custom element data is created at the first attempt to enqueue a callback.
*
* @return The custom element data or null if none.
*/
virtual mozilla::dom::CustomElementData *GetCustomElementData() const = 0;
/**
* Sets the custom element data, ownership of the
* callback data is taken by this content.
*
* @param aCallbackData The custom element data.
*/
virtual void SetCustomElementData(mozilla::dom::CustomElementData* aData) = 0;
/** /**
* API to check if this is a link that's traversed in response to user input * API to check if this is a link that's traversed in response to user input
* (e.g. a click event). Specializations for HTML/SVG/generic XML allow for * (e.g. a click event). Specializations for HTML/SVG/generic XML allow for

View File

@ -61,6 +61,7 @@ class nsFrameLoader;
class nsHTMLCSSStyleSheet; class nsHTMLCSSStyleSheet;
class nsHTMLDocument; class nsHTMLDocument;
class nsHTMLStyleSheet; class nsHTMLStyleSheet;
class nsGenericHTMLElement;
class nsIAtom; class nsIAtom;
class nsIBFCacheEntry; class nsIBFCacheEntry;
class nsIChannel; class nsIChannel;
@ -1036,6 +1037,11 @@ public:
Element* GetHeadElement() { Element* GetHeadElement() {
return GetHtmlChildElement(nsGkAtoms::head); return GetHtmlChildElement(nsGkAtoms::head);
} }
// Get the "body" in the sense of document.body: The first <body> or
// <frameset> that's a child of a root <html>
nsGenericHTMLElement* GetBody();
// Set the "body" in the sense of document.body.
void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
/** /**
* Accessors to the collection of stylesheets owned by this document. * Accessors to the collection of stylesheets owned by this document.
@ -2573,9 +2579,9 @@ public:
} }
enum ElementCallbackType { enum ElementCallbackType {
eCreated, eConnected,
eAttached, eDisconnected,
eDetached, eAdopted,
eAttributeChanged eAttributeChanged
}; };
@ -2872,6 +2878,22 @@ public:
virtual void ScheduleIntersectionObserverNotification() = 0; virtual void ScheduleIntersectionObserverNotification() = 0;
virtual void NotifyIntersectionObservers() = 0; virtual void NotifyIntersectionObservers() = 0;
bool ShouldThrowOnDynamicMarkupInsertion()
{
return mThrowOnDynamicMarkupInsertionCounter;
}
void IncrementThrowOnDynamicMarkupInsertionCounter()
{
++mThrowOnDynamicMarkupInsertionCounter;
}
void DecrementThrowOnDynamicMarkupInsertionCounter()
{
MOZ_ASSERT(mThrowOnDynamicMarkupInsertionCounter);
--mThrowOnDynamicMarkupInsertionCounter;
}
protected: protected:
bool GetUseCounter(mozilla::UseCounter aUseCounter) bool GetUseCounter(mozilla::UseCounter aUseCounter)
{ {
@ -3319,6 +3341,11 @@ protected:
uint32_t mBlockDOMContentLoaded; uint32_t mBlockDOMContentLoaded;
// Used in conjunction with the create-an-element-for-the-token algorithm to
// prevent custom element constructors from being able to use document.open(),
// document.close(), and document.write() when they are invoked by the parser.
uint32_t mThrowOnDynamicMarkupInsertionCounter;
// Our live MediaQueryLists // Our live MediaQueryLists
PRCList mDOMMediaQueryLists; PRCList mDOMMediaQueryLists;
@ -3392,6 +3419,23 @@ private:
uint32_t mMicroTaskLevel; uint32_t mMicroTaskLevel;
}; };
class MOZ_RAII AutoSetThrowOnDynamicMarkupInsertionCounter final {
public:
explicit AutoSetThrowOnDynamicMarkupInsertionCounter(
nsIDocument* aDocument)
: mDocument(aDocument)
{
mDocument->IncrementThrowOnDynamicMarkupInsertionCounter();
}
~AutoSetThrowOnDynamicMarkupInsertionCounter() {
mDocument->DecrementThrowOnDynamicMarkupInsertionCounter();
}
private:
nsIDocument* mDocument;
};
// XXX These belong somewhere else // XXX These belong somewhere else
nsresult nsresult
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false); NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);

View File

@ -25,7 +25,7 @@
#include "xpcpublic.h" #include "xpcpublic.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Date.h" #include "mozilla/dom/Date.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
@ -159,7 +159,8 @@ nsJSUtils::EvaluateString(JSContext* aCx,
aEvaluationGlobal); aEvaluationGlobal);
MOZ_ASSERT_IF(aOffThreadToken, aCompileOptions.noScriptRval); MOZ_ASSERT_IF(aOffThreadToken, aCompileOptions.noScriptRval);
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsInMicroTask()); MOZ_ASSERT(CycleCollectedJSContext::Get() &&
CycleCollectedJSContext::Get()->MicroTaskLevel());
// Unfortunately, the JS engine actually compiles scripts with a return value // Unfortunately, the JS engine actually compiles scripts with a return value
// in a different, less efficient way. Furthermore, it can't JIT them in many // in a different, less efficient way. Furthermore, it can't JIT them in many
@ -293,7 +294,8 @@ nsJSUtils::CompileModule(JSContext* aCx,
aEvaluationGlobal); aEvaluationGlobal);
MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal); MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsInMicroTask()); MOZ_ASSERT(CycleCollectedJSContext::Get() &&
CycleCollectedJSContext::Get()->MicroTaskLevel());
NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK); NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
@ -330,7 +332,8 @@ nsJSUtils::ModuleEvaluation(JSContext* aCx, JS::Handle<JSObject*> aModule)
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsInMicroTask()); MOZ_ASSERT(CycleCollectedJSContext::Get() &&
CycleCollectedJSContext::Get()->MicroTaskLevel());
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK); NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);

View File

@ -301,9 +301,12 @@ nsNodeUtils::LastRelease(nsINode* aNode)
Element* elem = aNode->AsElement(); Element* elem = aNode->AsElement();
FragmentOrElement::nsDOMSlots* domSlots = FragmentOrElement::nsDOMSlots* domSlots =
static_cast<FragmentOrElement::nsDOMSlots*>(slots); static_cast<FragmentOrElement::nsDOMSlots*>(slots);
for (auto iter = domSlots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { if (domSlots->mExtendedSlots) {
DOMIntersectionObserver* observer = iter.Key(); for (auto iter = domSlots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
observer->UnlinkTarget(*elem); !iter.Done(); iter.Next()) {
DOMIntersectionObserver* observer = iter.Key();
observer->UnlinkTarget(*elem);
}
} }
} }
@ -476,19 +479,33 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone)); rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (clone->IsElement()) { if (CustomElementRegistry::IsCustomElementEnabled() &&
clone->IsHTMLElement()) {
// The cloned node may be a custom element that may require // The cloned node may be a custom element that may require
// enqueing created callback and prototype swizzling. // enqueing upgrade reaction.
Element* elem = clone->AsElement(); Element* cloneElem = clone->AsElement();
if (nsContentUtils::IsCustomElementName(nodeInfo->NameAtom())) { RefPtr<nsIAtom> tagAtom = nodeInfo->NameAtom();
nsContentUtils::SetupCustomElement(elem); CustomElementData* data = elem->GetCustomElementData();
} else {
// Check if node may be custom element by type extension. // Check if node may be custom element by type extension.
// ex. <button is="x-button"> // ex. <button is="x-button">
nsAutoString extension; nsAutoString extension;
if (elem->GetAttr(kNameSpaceID_None, nsGkAtoms::is, extension) && if (!data || data->GetCustomElementType() != tagAtom) {
!extension.IsEmpty()) { cloneElem->GetAttr(kNameSpaceID_None, nsGkAtoms::is, extension);
nsContentUtils::SetupCustomElement(elem, &extension); }
if (data || !extension.IsEmpty()) {
RefPtr<nsIAtom> typeAtom = extension.IsEmpty() ? tagAtom : NS_Atomize(extension);
cloneElem->SetCustomElementData(new CustomElementData(typeAtom));
MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
CustomElementDefinition* definition =
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
nodeInfo->NameAtom(),
nodeInfo->NamespaceID(),
typeAtom);
if (definition) {
nsContentUtils::EnqueueUpgradeReaction(cloneElem, definition);
} }
} }
} }
@ -523,6 +540,23 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
nsIDocument* newDoc = aNode->OwnerDoc(); nsIDocument* newDoc = aNode->OwnerDoc();
if (newDoc) { if (newDoc) {
if (CustomElementRegistry::IsCustomElementEnabled()) {
// Adopted callback must be enqueued whenever a nodes
// shadow-including inclusive descendants that is custom.
Element* element = aNode->IsElement() ? aNode->AsElement() : nullptr;
if (element) {
CustomElementData* data = element->GetCustomElementData();
if (data && data->mState == CustomElementData::State::eCustom) {
LifecycleAdoptedCallbackArgs args = {
oldDoc,
newDoc
};
nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAdopted,
element, nullptr, &args);
}
}
}
// XXX what if oldDoc is null, we don't know if this should be // XXX what if oldDoc is null, we don't know if this should be
// registered or not! Can that really happen? // registered or not! Can that really happen?
if (wasRegistered) { if (wasRegistered) {

View File

@ -1,8 +1,8 @@
var proto = Object.create(HTMLElement.prototype); var proto = Object.create(HTMLElement.prototype);
proto.magicNumber = 42; proto.magicNumber = 42;
proto.createdCallback = function() { proto.connectedCallback = function() {
finishTest(this.magicNumber === 42); finishTest(this.magicNumber === 42);
}; };
document.registerElement("x-foo", { prototype: proto }); document.registerElement("x-foo", { prototype: proto });
document.createElement("x-foo"); document.firstChild.appendChild(document.createElement("x-foo"));

View File

@ -21,19 +21,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1130028
<script type="application/javascript"><![CDATA[ <script type="application/javascript"><![CDATA[
/** Test for Bug 1130028 **/ /** Test for Bug 1130028 **/
SimpleTest.waitForExplicitFinish(); var connectedCallbackCount = 0;
var createdCallbackCount = 0; // Callback should be called only once by element created in content.
function connectedCallbackCalled() {
// Callback should be called once by element created in chrome, connectedCallbackCount++;
// and once by element created in content. is(connectedCallbackCount, 1, "Connected callback called, should be called once in test.");
function createdCallbackCalled() {
createdCallbackCount++;
ok(true, "Created callback called, should be called twice in test.");
is(this.magicNumber, 42, "Callback should be able to see the custom prototype."); is(this.magicNumber, 42, "Callback should be able to see the custom prototype.");
if (createdCallbackCount == 2) {
SimpleTest.finish();
}
} }
function startTests() { function startTests() {
@ -45,10 +39,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1130028
var proto = Object.create(frame.contentWindow.HTMLElement.prototype); var proto = Object.create(frame.contentWindow.HTMLElement.prototype);
proto.magicNumber = 42; proto.magicNumber = 42;
proto.createdCallback = createdCallbackCalled; proto.connectedCallback = connectedCallbackCalled;
frame.contentDocument.registerElement("x-bar", { prototype: proto });
frame.contentDocument.createElement("x-bar"); frame.contentDocument.registerElement("x-bar", { prototype: proto });
is(connectedCallbackCount, 1, "Connected callback should be called by element created in content.");
var element = frame.contentDocument.createElement("x-bar");
is(element.magicNumber, 42, "Should be able to see the custom prototype on created element.");
} }
]]></script> ]]></script>

View File

@ -26,8 +26,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1130028
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
function finishTest(canSeePrototype) { function finishTest(canSeePrototype) {
ok(true, "createdCallback called when reigsterElement was called with an extended principal."); ok(true, "connectedCallback called when reigsterElement was called with an extended principal.");
ok(canSeePrototype, "createdCallback should be able to see custom prototype."); ok(canSeePrototype, "connectedCallback should be able to see custom prototype.");
SimpleTest.finish(); SimpleTest.finish();
} }

View File

@ -362,7 +362,7 @@ function testChildList5() {
is(records[5].previousSibling, c3, ""); is(records[5].previousSibling, c3, "");
is(records[5].nextSibling, c5, ""); is(records[5].nextSibling, c5, "");
observer.disconnect(); observer.disconnect();
then(testAdoptNode); then(testNestedMutations);
m = null; m = null;
}); });
m.observe(div, { childList: true, subtree: true }); m.observe(div, { childList: true, subtree: true });
@ -375,6 +375,37 @@ function testChildList5() {
div.appendChild(emptyDF); // empty document shouldn't cause mutation records div.appendChild(emptyDF); // empty document shouldn't cause mutation records
} }
function testNestedMutations() {
div.textContent = null;
div.appendChild(document.createTextNode("foo"));
var m2WasCalled = false;
m = new M(function(records, observer) {
is(records[0].type, "characterData", "Should have got characterData");
observer.disconnect();
m = null;
m3 = new M(function(records, observer) {
ok(m2WasCalled, "m2 should have been called before m3!");
is(records[0].type, "characterData", "Should have got characterData");
observer.disconnect();
then(testAdoptNode);
m3 = null;
});
m3.observe(div, { characterData: true, subtree: true});
div.firstChild.data = "foo";
});
m2 = new M(function(records, observer) {
m2WasCalled = true;
is(records[0].type, "characterData", "Should have got characterData");
observer.disconnect();
m2 = null;
});
m2.observe(div, { characterData: true, subtree: true});
div.appendChild(document.createTextNode("foo"));
m.observe(div, { characterData: true, subtree: true });
div.firstChild.data = "bar";
}
function testAdoptNode() { function testAdoptNode() {
var d1 = document.implementation.createHTMLDocument(null); var d1 = document.implementation.createHTMLDocument(null);
var d2 = document.implementation.createHTMLDocument(null); var d2 = document.implementation.createHTMLDocument(null);

View File

@ -16,13 +16,16 @@
#include "mozilla/SizePrintfMacros.h" #include "mozilla/SizePrintfMacros.h"
#include "mozilla/Unused.h" #include "mozilla/Unused.h"
#include "mozilla/UseCounter.h" #include "mozilla/UseCounter.h"
#include "mozilla/dom/DocGroup.h"
#include "AccessCheck.h" #include "AccessCheck.h"
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "nsIDocShell.h" #include "nsIDocShell.h"
#include "nsIDOMGlobalPropertyInitializer.h" #include "nsIDOMGlobalPropertyInitializer.h"
#include "nsIParserService.h"
#include "nsIPermissionManager.h" #include "nsIPermissionManager.h"
#include "nsIPrincipal.h" #include "nsIPrincipal.h"
#include "nsIXPConnect.h" #include "nsIXPConnect.h"
@ -37,6 +40,7 @@
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DOMError.h" #include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMErrorBinding.h" #include "mozilla/dom/DOMErrorBinding.h"
#include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMException.h"
@ -44,6 +48,7 @@
#include "mozilla/dom/HTMLObjectElement.h" #include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLObjectElementBinding.h" #include "mozilla/dom/HTMLObjectElementBinding.h"
#include "mozilla/dom/HTMLSharedObjectElement.h" #include "mozilla/dom/HTMLSharedObjectElement.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/dom/HTMLEmbedElementBinding.h" #include "mozilla/dom/HTMLEmbedElementBinding.h"
#include "mozilla/dom/HTMLAppletElementBinding.h" #include "mozilla/dom/HTMLAppletElementBinding.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
@ -62,6 +67,30 @@ namespace dom {
using namespace workers; using namespace workers;
// Forward declare GetConstructorObject methods.
#define HTML_TAG(_tag, _classname, _interfacename) \
namespace HTML##_interfacename##ElementBinding { \
JSObject* GetConstructorObject(JSContext*); \
}
#define HTML_OTHER(_tag)
#include "nsHTMLTagList.h"
#undef HTML_TAG
#undef HTML_OTHER
typedef JSObject* (*constructorGetterCallback)(JSContext*);
// Mapping of html tag and GetConstructorObject methods.
#define HTML_TAG(_tag, _classname, _interfacename) HTML##_interfacename##ElementBinding::GetConstructorObject,
#define HTML_OTHER(_tag) nullptr,
// We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h
// to index into this array.
static const constructorGetterCallback sConstructorGetterCallback[] = {
HTMLUnknownElementBinding::GetConstructorObject,
#include "nsHTMLTagList.h"
#undef HTML_TAG
#undef HTML_OTHER
};
const JSErrorFormatString ErrorFormatString[] = { const JSErrorFormatString ErrorFormatString[] = {
#define MSG_DEF(_name, _argc, _exn, _str) \ #define MSG_DEF(_name, _argc, _exn, _str) \
{ #_name, _str, _argc, _exn }, { #_name, _str, _argc, _exn },
@ -3377,6 +3406,189 @@ GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
return true; return true;
} }
CustomElementReactionsStack*
GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj)
{
// This might not be the right object, if there are wrappers. Unwrap if we can.
JSObject* obj = js::CheckedUnwrap(aObj);
if (!obj) {
return nullptr;
}
nsGlobalWindow* window = xpc::WindowGlobalOrNull(obj);
if (!window) {
return nullptr;
}
DocGroup* docGroup = window->AsInner()->GetDocGroup();
if (!docGroup) {
return nullptr;
}
return docGroup->CustomElementReactionsStack();
}
// https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
already_AddRefed<nsGenericHTMLElement>
CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv)
{
// Step 1.
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
if (!window) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsIDocument* doc = window->GetExtantDoc();
if (!doc) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
RefPtr<mozilla::dom::CustomElementRegistry> registry(window->CustomElements());
if (!registry) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
// Step 2 is in the code output by CGClassConstructor.
// Step 3.
JSContext* cx = aGlobal.Context();
JS::Rooted<JSObject*> newTarget(cx, &aCallArgs.newTarget().toObject());
CustomElementDefinition* definition =
registry->LookupCustomElementDefinition(cx, newTarget);
if (!definition) {
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
return nullptr;
}
// The callee might be an Xray. Unwrap it to get actual callee.
JS::Rooted<JSObject*> callee(cx, js::CheckedUnwrap(&aCallArgs.callee()));
if (!callee) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
// And the actual callee might be in different compartment, so enter its
// compartment before getting the standard constructor object to compare to,
// so we get it from the same global as callee itself.
JSAutoCompartment ac(cx, callee);
int32_t tag = eHTMLTag_userdefined;
if (!definition->IsCustomBuiltIn()) {
// Step 4.
// If the definition is for an autonomous custom element, the active
// function should be HTMLElement.
JS::Rooted<JSObject*> constructor(cx, HTMLElementBinding::GetConstructorObject(cx));
if (!constructor) {
aRv.NoteJSContextException(cx);
return nullptr;
}
if (callee != constructor) {
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
return nullptr;
}
} else {
// Step 5.
// If the definition is for a customized built-in element, the localName
// should be defined in the specification.
nsIParserService* parserService = nsContentUtils::GetParserService();
if (!parserService) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
tag = parserService->HTMLCaseSensitiveAtomTagToId(definition->mLocalName);
if (tag == eHTMLTag_userdefined) {
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
return nullptr;
}
MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
// If the definition is for a customized built-in element, the active
// function should be the localname's element interface.
constructorGetterCallback cb = sConstructorGetterCallback[tag];
if (!cb) {
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
return nullptr;
}
JS::Rooted<JSObject*> constructor(cx, cb(cx));
if (!constructor) {
aRv.NoteJSContextException(cx);
return nullptr;
}
if (callee != constructor) {
aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
return nullptr;
}
}
RefPtr<mozilla::dom::NodeInfo> nodeInfo =
doc->NodeInfoManager()->GetNodeInfo(definition->mLocalName,
nullptr,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);
if (!nodeInfo) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
// Step 6 and Step 7 are in the code output by CGClassConstructor.
// Step 8.
nsTArray<RefPtr<nsGenericHTMLElement>>& constructionStack =
definition->mConstructionStack;
if (constructionStack.IsEmpty()) {
RefPtr<nsGenericHTMLElement> newElement;
if (tag == eHTMLTag_userdefined) {
// Autonomous custom element.
newElement = NS_NewHTMLElement(nodeInfo.forget());
} else {
// Customized built-in element.
newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
}
newElement->SetCustomElementData(
new CustomElementData(definition->mType, CustomElementData::State::eCustom));
newElement->SetCustomElementDefinition(definition);
return newElement.forget();
}
// Step 9.
RefPtr<nsGenericHTMLElement>& element = constructionStack.LastElement();
// Step 10.
if (element == ALEADY_CONSTRUCTED_MARKER) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
// Step 11.
// Do prototype swizzling for upgrading a custom element here, for cases when
// we have a reflector already. If we don't have one yet, our caller will
// create it with the right proto (by calling DoGetOrCreateDOMReflector with
// that proto).
JS::Rooted<JSObject*> reflector(cx, element->GetWrapper());
if (reflector) {
// reflector might be in different compartment.
JSAutoCompartment ac(cx, reflector);
JS::Rooted<JSObject*> givenProto(cx, aGivenProto);
if (!JS_WrapObject(cx, &givenProto) ||
!JS_SetPrototype(cx, reflector, givenProto)) {
aRv.NoteJSContextException(cx);
return nullptr;
}
}
// Step 12 and Step 13.
return element.forget();
}
#ifdef DEBUG #ifdef DEBUG
namespace binding_detail { namespace binding_detail {
void void

View File

@ -42,6 +42,7 @@
#include "nsWrapperCacheInlines.h" #include "nsWrapperCacheInlines.h"
class nsGenericHTMLElement;
class nsIJSID; class nsIJSID;
namespace mozilla { namespace mozilla {
@ -49,6 +50,7 @@ namespace mozilla {
enum UseCounter : int16_t; enum UseCounter : int16_t;
namespace dom { namespace dom {
class CustomElementReactionsStack;
template<typename KeyType, typename ValueType> class Record; template<typename KeyType, typename ValueType> class Record;
nsresult nsresult
@ -3420,6 +3422,19 @@ bool
GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
JS::MutableHandle<JSObject*> aDesiredProto); JS::MutableHandle<JSObject*> aDesiredProto);
// Get the CustomElementReactionsStack for the docgroup of the global
// of the underlying object of aObj. This can be null if aObj can't
// be CheckUnwrapped, or if the global of the result has no docgroup
// (e.g. because it's not a Window global).
CustomElementReactionsStack*
GetCustomElementReactionsStack(JS::Handle<JSObject*> aObj);
// This function is expected to be called from the constructor function for an
// HTML element interface; the global/callargs need to be whatever was passed to
// that constructor function.
already_AddRefed<nsGenericHTMLElement>
CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
void void
SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject, SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
UseCounter aUseCounter); UseCounter aUseCounter);

View File

@ -1638,6 +1638,15 @@ DOMInterfaces = {
'register': False, 'register': False,
}, },
'TestHTMLConstructorInterface' : {
'headerFile': 'TestBindingHeader.h',
'register': False,
},
'TestCEReactionsInterface' : {
'headerFile': 'TestBindingHeader.h',
'register': False,
},
} }
# These are temporary, until they've been converted to use new DOM bindings # These are temporary, until they've been converted to use new DOM bindings

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/CallbackObject.h" #include "mozilla/dom/CallbackObject.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/BindingUtils.h"
#include "jsfriendapi.h" #include "jsfriendapi.h"
#include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObject.h"
@ -79,7 +80,10 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
, mIsMainThread(NS_IsMainThread()) , mIsMainThread(NS_IsMainThread())
{ {
if (mIsMainThread) { if (mIsMainThread) {
nsContentUtils::EnterMicroTask(); CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
ccjs->EnterMicroTask();
}
} }
// Compute the caller's subject principal (if necessary) early, before we // Compute the caller's subject principal (if necessary) early, before we
@ -288,7 +292,10 @@ CallbackObject::CallSetup::~CallSetup()
// It is important that this is the last thing we do, after leaving the // It is important that this is the last thing we do, after leaving the
// compartment and undoing all our entry/incumbent script changes // compartment and undoing all our entry/incumbent script changes
if (mIsMainThread) { if (mIsMainThread) {
nsContentUtils::LeaveMicroTask(); CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
ccjs->LeaveMicroTask();
}
} }
} }

View File

@ -1747,6 +1747,71 @@ class CGClassConstructor(CGAbstractStaticMethod):
else: else:
ctorName = self.descriptor.interface.identifier.name ctorName = self.descriptor.interface.identifier.name
# [HTMLConstructor] for custom element
# This needs to live in bindings code because it directly examines
# newtarget and the callee function to do HTMLConstructor specific things.
if self._ctor.isHTMLConstructor():
htmlConstructorSanityCheck = dedent("""
// The newTarget might be a cross-compartment wrapper. Get the underlying object
// so we can do the spec's object-identity checks.
JS::Rooted<JSObject*> newTarget(cx, js::CheckedUnwrap(&args.newTarget().toObject()));
if (!newTarget) {
return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
}
// Step 2 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
// Enter the compartment of our underlying newTarget object, so we end
// up comparing to the constructor object for our interface from that global.
{
JSAutoCompartment ac(cx, newTarget);
JS::Handle<JSObject*> constructor(GetConstructorObjectHandle(cx));
if (!constructor) {
return false;
}
if (newTarget == constructor) {
return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
}
}
""")
# If we are unable to get desired prototype from newTarget, then we
# fall back to the interface prototype object from newTarget's realm.
htmlConstructorFallback = dedent("""
if (!desiredProto) {
// Step 7 of https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor.
// This fallback behavior is designed to match analogous behavior for the
// JavaScript built-ins. So we enter the compartment of our underlying
// newTarget object and fall back to the prototype object from that global.
// XXX The spec says to use GetFunctionRealm(), which is not actually
// the same thing as what we have here (e.g. in the case of scripted callable proxies
// whose target is not same-compartment with the proxy, or bound functions, etc).
// https://bugzilla.mozilla.org/show_bug.cgi?id=1317658
{
JSAutoCompartment ac(cx, newTarget);
desiredProto = GetProtoObjectHandle(cx);
if (!desiredProto) {
return false;
}
}
// desiredProto is in the compartment of the underlying newTarget object.
// Wrap it into the context compartment.
if (!JS_WrapObject(cx, &desiredProto)) {
return false;
}
}
""")
else:
htmlConstructorSanityCheck = ""
htmlConstructorFallback = ""
# If we're a constructor, "obj" may not be a function, so calling
# XrayAwareCalleeGlobal() on it is not safe. Of course in the
# constructor case either "obj" is an Xray or we're already in the
# content compartment, not the Xray compartment, so just
# constructing the GlobalObject from "obj" is fine.
preamble = fill( preamble = fill(
""" """
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@ -1757,19 +1822,41 @@ class CGClassConstructor(CGAbstractStaticMethod):
// Adding more relocations // Adding more relocations
return ThrowConstructorWithoutNew(cx, "${ctorName}"); return ThrowConstructorWithoutNew(cx, "${ctorName}");
} }
GlobalObject global(cx, obj);
if (global.Failed()) {
return false;
}
$*{htmlConstructorSanityCheck}
JS::Rooted<JSObject*> desiredProto(cx); JS::Rooted<JSObject*> desiredProto(cx);
if (!GetDesiredProto(cx, args, &desiredProto)) { if (!GetDesiredProto(cx, args, &desiredProto)) {
return false; return false;
} }
$*{htmlConstructorFallback}
""", """,
chromeOnlyCheck=chromeOnlyCheck, chromeOnlyCheck=chromeOnlyCheck,
ctorName=ctorName) ctorName=ctorName,
htmlConstructorSanityCheck=htmlConstructorSanityCheck,
htmlConstructorFallback=htmlConstructorFallback)
name = self._ctor.identifier.name if self._ctor.isHTMLConstructor():
nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) signatures = self._ctor.signatures()
callGenerator = CGMethodCall(nativeName, True, self.descriptor, assert len(signatures) == 1
self._ctor, isConstructor=True, # Given that HTMLConstructor takes no args, we can just codegen a
constructorName=ctorName) # call to CreateHTMLElement() in BindingUtils which reuses the
# factory thing in HTMLContentSink. Then we don't have to implement
# Constructor on all the HTML elements.
callGenerator = CGPerSignatureCall(signatures[0][0], signatures[0][1],
"CreateHTMLElement", True,
self.descriptor, self._ctor,
isConstructor=True)
else:
name = self._ctor.identifier.name
nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
callGenerator = CGMethodCall(nativeName, True, self.descriptor,
self._ctor, isConstructor=True,
constructorName=ctorName)
return preamble + "\n" + callGenerator.define() return preamble + "\n" + callGenerator.define()
@ -7386,7 +7473,7 @@ class CGPerSignatureCall(CGThing):
def __init__(self, returnType, arguments, nativeMethodName, static, def __init__(self, returnType, arguments, nativeMethodName, static,
descriptor, idlNode, argConversionStartsAt=0, getter=False, descriptor, idlNode, argConversionStartsAt=0, getter=False,
setter=False, isConstructor=False, useCounterName=None, setter=False, isConstructor=False, useCounterName=None,
resultVar=None): resultVar=None, objectName="obj"):
assert idlNode.isMethod() == (not getter and not setter) assert idlNode.isMethod() == (not getter and not setter)
assert idlNode.isAttr() == (getter or setter) assert idlNode.isAttr() == (getter or setter)
# Constructors are always static # Constructors are always static
@ -7440,26 +7527,23 @@ class CGPerSignatureCall(CGThing):
argsPre = [] argsPre = []
if idlNode.isStatic(): if idlNode.isStatic():
# If we're a constructor, "obj" may not be a function, so calling # If we're a constructor, the GlobalObject struct will be created in
# XrayAwareCalleeGlobal() on it is not safe. Of course in the # CGClassConstructor.
# constructor case either "obj" is an Xray or we're already in the if not isConstructor:
# content compartment, not the Xray compartment, so just cgThings.append(CGGeneric(dedent(
# constructing the GlobalObject from "obj" is fine. """
if isConstructor: GlobalObject global(cx, xpc::XrayAwareCalleeGlobal(obj));
objForGlobalObject = "obj" if (global.Failed()) {
else: return false;
objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)" }
cgThings.append(CGGeneric(fill(
""" """)))
GlobalObject global(cx, ${obj});
if (global.Failed()) {
return false;
}
""",
obj=objForGlobalObject)))
argsPre.append("global") argsPre.append("global")
if isConstructor and idlNode.isHTMLConstructor():
argsPre.extend(["args", "desiredProto"])
# For JS-implemented interfaces we do not want to base the # For JS-implemented interfaces we do not want to base the
# needsCx decision on the types involved, just on our extended # needsCx decision on the types involved, just on our extended
# attributes. Also, JSContext is not needed for the static case # attributes. Also, JSContext is not needed for the static case
@ -7588,6 +7672,17 @@ class CGPerSignatureCall(CGThing):
CGIfWrapper(CGList(xraySteps), CGIfWrapper(CGList(xraySteps),
"objIsXray")) "objIsXray"))
if (idlNode.getExtendedAttribute('CEReactions') is not None and
not getter):
cgThings.append(CGGeneric(fill(
"""
CustomElementReactionsStack* reactionsStack = GetCustomElementReactionsStack(${obj});
Maybe<AutoCEReaction> ceReaction;
if (reactionsStack) {
ceReaction.emplace(reactionsStack, cx);
}
""", obj=objectName)))
# If this is a method that was generated by a maplike/setlike # If this is a method that was generated by a maplike/setlike
# interface, use the maplike/setlike generator to fill in the body. # interface, use the maplike/setlike generator to fill in the body.
# Otherwise, use CGCallGenerator to call the native method. # Otherwise, use CGCallGenerator to call the native method.
@ -10985,7 +11080,8 @@ class CGProxySpecialOperation(CGPerSignatureCall):
# CGPerSignatureCall won't do any argument conversion of its own. # CGPerSignatureCall won't do any argument conversion of its own.
CGPerSignatureCall.__init__(self, returnType, arguments, nativeName, CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
False, descriptor, operation, False, descriptor, operation,
len(arguments), resultVar=resultVar) len(arguments), resultVar=resultVar,
objectName="proxy")
if operation.isSetter() or operation.isCreator(): if operation.isSetter() or operation.isCreator():
# arguments[0] is the index or name of the item that we're setting. # arguments[0] is the index or name of the item that we're setting.
@ -13755,12 +13851,18 @@ class CGBindingRoot(CGThing):
iface = desc.interface iface = desc.interface
return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]) return any(m.getExtendedAttribute("Deprecated") for m in iface.members + [iface])
def descriptorHasCEReactions(desc):
iface = desc.interface
return any(m.getExtendedAttribute("CEReactions") for m in iface.members + [iface])
bindingHeaders["nsIDocument.h"] = any( bindingHeaders["nsIDocument.h"] = any(
descriptorDeprecated(d) for d in descriptors) descriptorDeprecated(d) for d in descriptors)
bindingHeaders["mozilla/Preferences.h"] = any( bindingHeaders["mozilla/Preferences.h"] = any(
descriptorRequiresPreferences(d) for d in descriptors) descriptorRequiresPreferences(d) for d in descriptors)
bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
d.concrete and d.proxy for d in descriptors) d.concrete and d.proxy for d in descriptors)
bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = any(
descriptorHasCEReactions(d) for d in descriptors)
def descriptorHasChromeOnly(desc): def descriptorHasChromeOnly(desc):
ctor = desc.interface.ctor() ctor = desc.interface.ctor()

View File

@ -1582,7 +1582,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
[self.location]) [self.location])
self._noInterfaceObject = True self._noInterfaceObject = True
elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor": elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
if identifier == "Constructor" and not self.hasInterfaceObject(): if identifier == "Constructor" and not self.hasInterfaceObject():
raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
[self.location]) [self.location])
@ -1595,11 +1595,20 @@ class IDLInterface(IDLInterfaceOrNamespace):
raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible", raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
[self.location]) [self.location])
if identifier == "HTMLConstructor":
if not self.hasInterfaceObject():
raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
[self.location])
if not attr.noArguments():
raise WebIDLError(str(identifier) + " must take no arguments",
[attr.location])
args = attr.args() if attr.hasArgs() else [] args = attr.args() if attr.hasArgs() else []
retType = IDLWrapperType(self.location, self) retType = IDLWrapperType(self.location, self)
if identifier == "Constructor" or identifier == "ChromeConstructor": if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
name = "constructor" name = "constructor"
allowForbidden = True allowForbidden = True
else: else:
@ -1610,7 +1619,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
allowForbidden=allowForbidden) allowForbidden=allowForbidden)
method = IDLMethod(self.location, methodIdentifier, retType, method = IDLMethod(self.location, methodIdentifier, retType,
args, static=True) args, static=True,
htmlConstructor=(identifier == "HTMLConstructor"))
# Constructors are always NewObject and are always # Constructors are always NewObject and are always
# assumed to be able to throw (since there's no way to # assumed to be able to throw (since there's no way to
# indicate otherwise) and never have any other # indicate otherwise) and never have any other
@ -1622,7 +1632,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
method.addExtendedAttributes( method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("ChromeOnly",))]) [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
if identifier == "Constructor" or identifier == "ChromeConstructor": if identifier == "Constructor" or identifier == "ChromeConstructor" or identifier == "HTMLConstructor":
method.resolve(self) method.resolve(self)
else: else:
# We need to detect conflicts for NamedConstructors across # We need to detect conflicts for NamedConstructors across
@ -4073,6 +4083,11 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("Attribute returns a type that is not exposed " raise WebIDLError("Attribute returns a type that is not exposed "
"everywhere where the attribute is exposed", "everywhere where the attribute is exposed",
[self.location]) [self.location])
if self.getExtendedAttribute("CEReactions"):
if self.readonly:
raise WebIDLError("[CEReactions] is not allowed on "
"readonly attributes",
[self.location])
def handleExtendedAttribute(self, attr): def handleExtendedAttribute(self, attr):
identifier = attr.identifier() identifier = attr.identifier()
@ -4243,6 +4258,10 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[Unscopable] is only allowed on non-static " raise WebIDLError("[Unscopable] is only allowed on non-static "
"attributes and operations", "attributes and operations",
[attr.location, self.location]) [attr.location, self.location])
elif identifier == "CEReactions":
if not attr.noArguments():
raise WebIDLError("[CEReactions] must take no arguments",
[attr.location])
elif (identifier == "Pref" or elif (identifier == "Pref" or
identifier == "Deprecated" or identifier == "Deprecated" or
identifier == "SetterThrows" or identifier == "SetterThrows" or
@ -4537,7 +4556,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
static=False, getter=False, setter=False, creator=False, static=False, getter=False, setter=False, creator=False,
deleter=False, specialType=NamedOrIndexed.Neither, deleter=False, specialType=NamedOrIndexed.Neither,
legacycaller=False, stringifier=False, jsonifier=False, legacycaller=False, stringifier=False, jsonifier=False,
maplikeOrSetlikeOrIterable=None): maplikeOrSetlikeOrIterable=None, htmlConstructor=False):
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Method) IDLInterfaceMember.Tags.Method)
@ -4567,6 +4586,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._jsonifier = jsonifier self._jsonifier = jsonifier
assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase) assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
assert isinstance(htmlConstructor, bool)
# The identifier of a HTMLConstructor must be 'constructor'.
assert not htmlConstructor or identifier.name == "constructor"
self._htmlConstructor = htmlConstructor
self._specialType = specialType self._specialType = specialType
self._unforgeable = False self._unforgeable = False
self.dependsOn = "Everything" self.dependsOn = "Everything"
@ -4667,6 +4690,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self.isStringifier() or self.isStringifier() or
self.isJsonifier()) self.isJsonifier())
def isHTMLConstructor(self):
return self._htmlConstructor
def hasOverloads(self): def hasOverloads(self):
return self._hasOverloads return self._hasOverloads
@ -4722,6 +4748,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not method.isStringifier() assert not method.isStringifier()
assert not self.isJsonifier() assert not self.isJsonifier()
assert not method.isJsonifier() assert not method.isJsonifier()
assert not self.isHTMLConstructor()
assert not method.isHTMLConstructor()
return self return self
@ -4964,6 +4992,15 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
raise WebIDLError("[Unscopable] is only allowed on non-static " raise WebIDLError("[Unscopable] is only allowed on non-static "
"attributes and operations", "attributes and operations",
[attr.location, self.location]) [attr.location, self.location])
elif identifier == "CEReactions":
if not attr.noArguments():
raise WebIDLError("[CEReactions] must take no arguments",
[attr.location])
if self.isSpecial() and not self.isSetter() and not self.isDeleter():
raise WebIDLError("[CEReactions] is only allowed on operation, "
"attribute, setter, and deleter",
[attr.location, self.location])
elif (identifier == "Throws" or elif (identifier == "Throws" or
identifier == "NewObject" or identifier == "NewObject" or
identifier == "ChromeOnly" or identifier == "ChromeOnly" or

View File

@ -0,0 +1,162 @@
def WebIDLTest(parser, harness):
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions(DOMString a)] void foo(boolean arg2);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions(DOMString b)] readonly attribute boolean bar;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] with an argument")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] attribute boolean bar;
};
""")
results = parser.finish()
except Exception, e:
harness.ok(False, "Shouldn't have thrown for [CEReactions] used on writable attribute. %s" % e)
threw = True
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] void foo(boolean arg2);
};
""")
results = parser.finish()
except Exception, e:
harness.ok(False, "Shouldn't have thrown for [CEReactions] used on regular operations. %s" % e)
threw = True
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] readonly attribute boolean A;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a readonly attribute")
parser = parser.reset()
threw = False
try:
parser.parse("""
[CEReactions]
interface Foo {
}
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a interface")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] getter any(DOMString name);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [CEReactions] used on a named getter")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] creator boolean (DOMString name, boolean value);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [CEReactions] used on a named creator")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] legacycaller double compute(double x);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [CEReactions] used on a legacycaller")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] stringifier DOMString ();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Should have thrown for [CEReactions] used on a stringifier")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface Foo {
[CEReactions] jsonifier;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown for [CEReactions] used on a jsonifier")

View File

@ -13,7 +13,7 @@ def WebIDLTest(parser, harness):
def checkMethod(method, QName, name, signatures, def checkMethod(method, QName, name, signatures,
static=True, getter=False, setter=False, creator=False, static=True, getter=False, setter=False, creator=False,
deleter=False, legacycaller=False, stringifier=False, deleter=False, legacycaller=False, stringifier=False,
chromeOnly=False): chromeOnly=False, htmlConstructor=False):
harness.ok(isinstance(method, WebIDL.IDLMethod), harness.ok(isinstance(method, WebIDL.IDLMethod),
"Should be an IDLMethod") "Should be an IDLMethod")
harness.ok(method.isMethod(), "Method is a method") harness.ok(method.isMethod(), "Method is a method")
@ -29,6 +29,7 @@ def WebIDLTest(parser, harness):
harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value") harness.check(method.isLegacycaller(), legacycaller, "Method has the correct legacycaller value")
harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value") harness.check(method.isStringifier(), stringifier, "Method has the correct stringifier value")
harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly") harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly")
harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value")
harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures") harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures")
sigpairs = zip(method.signatures(), signatures) sigpairs = zip(method.signatures(), signatures)
@ -78,6 +79,21 @@ def WebIDLTest(parser, harness):
("TestConstructorOverloads (Wrapper)", ("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])]) [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
parser = parser.reset()
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructor {
};
""")
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
checkMethod(results[0].ctor(), "::TestHTMLConstructor::constructor",
"constructor", [("TestHTMLConstructor (Wrapper)", [])],
htmlConstructor=True)
parser = parser.reset() parser = parser.reset()
parser.parse(""" parser.parse("""
[ChromeConstructor()] [ChromeConstructor()]
@ -107,3 +123,151 @@ def WebIDLTest(parser, harness):
threw = True threw = True
harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor") harness.ok(threw, "Can't have both a Constructor and a ChromeConstructor")
# Test HTMLConstructor with argument
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor(DOMString a)]
interface TestHTMLConstructorWithArgs {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "HTMLConstructor should take no argument")
# Test HTMLConstructor on a callback interface
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
callback interface TestHTMLConstructorOnCallbackInterface {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "HTMLConstructor can't be used on a callback interface")
# Test HTMLConstructor and Constructor
parser = parser.reset()
threw = False
try:
parser.parse("""
[Constructor,
HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a Constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
Constructor]
interface TestHTMLConstructorAndConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
Constructor(DOMString a)]
interface TestHTMLConstructorAndConstructor {
};
""")
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[Constructor(DOMString a),
HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
};
""")
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
# Test HTMLConstructor and ChromeConstructor
parser = parser.reset()
threw = False
try:
parser.parse("""
[ChromeConstructor,
HTMLConstructor]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
ChromeConstructor]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[ChromeConstructor(DOMString a),
HTMLConstructor]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor,
ChromeConstructor(DOMString a)]
interface TestHTMLConstructorAndChromeConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")

View File

@ -34,3 +34,36 @@ def WebIDLTest(parser, harness):
interface TestNamedConstructorNoInterfaceObject { interface TestNamedConstructorNoInterfaceObject {
}; };
""") """)
# Test HTMLConstructor and NoInterfaceObject
parser = parser.reset()
threw = False
try:
parser.parse("""
[NoInterfaceObject, HTMLConstructor]
interface TestHTMLConstructorNoInterfaceObject {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor, NoInterfaceObject]
interface TestHTMLConstructorNoInterfaceObject {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown.")

View File

@ -939,6 +939,11 @@ public:
void NeedsCallerTypeMethod(CallerType); void NeedsCallerTypeMethod(CallerType);
bool NeedsCallerTypeAttr(CallerType); bool NeedsCallerTypeAttr(CallerType);
void SetNeedsCallerTypeAttr(bool, CallerType); void SetNeedsCallerTypeAttr(bool, CallerType);
void CeReactionsMethod();
void CeReactionsMethodOverload();
void CeReactionsMethodOverload(const nsAString&);
bool CeReactionsAttr() const;
void SetCeReactionsAttr(bool);
int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&); int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&, void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
TestInterface*, const Dict&, double, TestInterface*, const Dict&, double,
@ -1425,6 +1430,31 @@ public:
void SetNeedsCallerTypeAttr(bool, CallerType); void SetNeedsCallerTypeAttr(bool, CallerType);
}; };
class TestHTMLConstructorInterface : public nsGenericHTMLElement
{
public:
virtual nsISupports* GetParentObject();
};
class TestCEReactionsInterface : public nsISupports,
public nsWrapperCache
{
public:
NS_DECL_ISUPPORTS
// We need a GetParentObject to make binding codegen happy
virtual nsISupports* GetParentObject();
int32_t Item(uint32_t);
uint32_t Length() const;
int32_t IndexedGetter(uint32_t, bool &);
void IndexedSetter(uint32_t, int32_t);
void NamedDeleter(const nsAString&, bool &);
void NamedGetter(const nsAString&, bool &, nsString&);
void NamedSetter(const nsAString&, const nsAString&);
void GetSupportedNames(nsTArray<nsString>&);
};
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View File

@ -947,6 +947,10 @@ interface TestInterface {
[NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr; [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
[NeedsCallerType] void needsCallerTypeMethod(); [NeedsCallerType] void needsCallerTypeMethod();
[NeedsCallerType] attribute boolean needsCallerTypeAttr; [NeedsCallerType] attribute boolean needsCallerTypeAttr;
[CEReactions] void ceReactionsMethod();
[CEReactions] void ceReactionsMethodOverload();
[CEReactions] void ceReactionsMethodOverload(DOMString bar);
[CEReactions] attribute boolean ceReactionsAttr;
legacycaller short(unsigned long arg1, TestInterface arg2); legacycaller short(unsigned long arg1, TestInterface arg2);
void passArgsWithDefaults(optional long arg1, void passArgsWithDefaults(optional long arg1,
optional TestInterface? arg2 = null, optional TestInterface? arg2 = null,
@ -1262,3 +1266,16 @@ interface TestWorkerExposedInterface {
[NeedsCallerType] void needsCallerTypeMethod(); [NeedsCallerType] void needsCallerTypeMethod();
[NeedsCallerType] attribute boolean needsCallerTypeAttr; [NeedsCallerType] attribute boolean needsCallerTypeAttr;
}; };
[HTMLConstructor]
interface TestHTMLConstructorInterface {
};
interface TestCEReactionsInterface {
[CEReactions] setter creator void (unsigned long index, long item);
[CEReactions] setter creator void (DOMString name, DOMString item);
[CEReactions] deleter void (DOMString name);
getter long item(unsigned long index);
getter DOMString (DOMString name);
readonly attribute unsigned long length;
};

View File

@ -777,6 +777,10 @@ interface TestExampleInterface {
[NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr; [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
[NeedsCallerType] void needsCallerTypeMethod(); [NeedsCallerType] void needsCallerTypeMethod();
[NeedsCallerType] attribute boolean needsCallerTypeAttr; [NeedsCallerType] attribute boolean needsCallerTypeAttr;
[CEReactions] void ceReactionsMethod();
[CEReactions] void ceReactionsMethodOverload();
[CEReactions] void ceReactionsMethodOverload(DOMString bar);
[CEReactions] attribute boolean ceReactionsAttr;
legacycaller short(unsigned long arg1, TestInterface arg2); legacycaller short(unsigned long arg1, TestInterface arg2);
void passArgsWithDefaults(optional long arg1, void passArgsWithDefaults(optional long arg1,
optional TestInterface? arg2 = null, optional TestInterface? arg2 = null,

View File

@ -794,6 +794,10 @@ interface TestJSImplInterface {
[Throws] attribute boolean throwingAttr; [Throws] attribute boolean throwingAttr;
[GetterThrows] attribute boolean throwingGetterAttr; [GetterThrows] attribute boolean throwingGetterAttr;
[SetterThrows] attribute boolean throwingSetterAttr; [SetterThrows] attribute boolean throwingSetterAttr;
[CEReactions] void ceReactionsMethod();
[CEReactions] void ceReactionsMethodOverload();
[CEReactions] void ceReactionsMethodOverload(DOMString bar);
[CEReactions] attribute boolean ceReactionsAttr;
// NeedsSubjectPrincipal not supported on JS-implemented things for // NeedsSubjectPrincipal not supported on JS-implemented things for
// now, because we always pass in the caller principal anyway. // now, because we always pass in the caller principal anyway.
// [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod(); // [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();

View File

@ -20,11 +20,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=560072
/** Test for Bug 560072 **/ /** Test for Bug 560072 **/
is(document.body, is(document.body,
Object.getOwnPropertyDescriptor(HTMLDocument.prototype, "body").get.call(document), Object.getOwnPropertyDescriptor(Document.prototype, "body").get.call(document),
"Should get body out of property descriptor"); "Should get body out of property descriptor");
is(document.body, is(document.body,
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(document), "body").get.call(document), Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(Object.getPrototypeOf(document)), "body").get.call(document),
"Should get body out of property descriptor this way too"); "Should get body out of property descriptor this way too");
</script> </script>

View File

@ -1087,7 +1087,10 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
if (NS_SUCCEEDED(result)) { if (NS_SUCCEEDED(result)) {
if (mIsMainThreadELM) { if (mIsMainThreadELM) {
nsContentUtils::EnterMicroTask(); CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
ccjs->EnterMicroTask();
}
} }
// nsIDOMEvent::currentTarget is set in EventDispatcher. // nsIDOMEvent::currentTarget is set in EventDispatcher.
if (listenerHolder.HasWebIDLCallback()) { if (listenerHolder.HasWebIDLCallback()) {
@ -1099,7 +1102,10 @@ EventListenerManager::HandleEventSubType(Listener* aListener,
result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent); result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
} }
if (mIsMainThreadELM) { if (mIsMainThreadELM) {
nsContentUtils::LeaveMicroTask(); CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
if (ccjs) {
ccjs->LeaveMicroTask();
}
} }
} }

View File

@ -16,6 +16,7 @@
#include "mozilla/dom/FlyWebDiscoveryManager.h" #include "mozilla/dom/FlyWebDiscoveryManager.h"
#include "mozilla/dom/FlyWebDiscoveryManagerBinding.h" #include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
#include "mozilla/dom/Element.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {

View File

@ -6,39 +6,11 @@
#include "mozilla/dom/HTMLDetailsElement.h" #include "mozilla/dom/HTMLDetailsElement.h"
#include "mozilla/dom/HTMLDetailsElementBinding.h" #include "mozilla/dom/HTMLDetailsElementBinding.h"
#include "mozilla/dom/HTMLUnknownElement.h" NS_IMPL_NS_NEW_HTML_ELEMENT(Details)
#include "mozilla/Preferences.h"
// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Details) to add pref check.
nsGenericHTMLElement*
NS_NewHTMLDetailsElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser)
{
if (!mozilla::dom::HTMLDetailsElement::IsDetailsEnabled()) {
return new mozilla::dom::HTMLUnknownElement(aNodeInfo);
}
return new mozilla::dom::HTMLDetailsElement(aNodeInfo);
}
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
/* static */ bool
HTMLDetailsElement::IsDetailsEnabled()
{
static bool isDetailsEnabled = false;
static bool added = false;
if (!added) {
Preferences::AddBoolVarCache(&isDetailsEnabled,
"dom.details_element.enabled");
added = true;
}
return isDetailsEnabled;
}
HTMLDetailsElement::~HTMLDetailsElement() HTMLDetailsElement::~HTMLDetailsElement()
{ {
} }

View File

@ -23,8 +23,6 @@ class HTMLDetailsElement final : public nsGenericHTMLElement
public: public:
using NodeInfo = mozilla::dom::NodeInfo; using NodeInfo = mozilla::dom::NodeInfo;
static bool IsDetailsEnabled();
explicit HTMLDetailsElement(already_AddRefed<NodeInfo>& aNodeInfo) explicit HTMLDetailsElement(already_AddRefed<NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo) : nsGenericHTMLElement(aNodeInfo)
{ {

View File

@ -52,3 +52,12 @@ NS_NewHTMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
{ {
return new mozilla::dom::HTMLElement(aNodeInfo); return new mozilla::dom::HTMLElement(aNodeInfo);
} }
// Distinct from the above in order to have function pointer that compared unequal
// to a function pointer to the above.
nsGenericHTMLElement*
NS_NewCustomElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser)
{
return new mozilla::dom::HTMLElement(aNodeInfo);
}

View File

@ -14,17 +14,7 @@
#include "mozilla/TextEvents.h" #include "mozilla/TextEvents.h"
#include "nsFocusManager.h" #include "nsFocusManager.h"
// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Summary) to add pref check. NS_IMPL_NS_NEW_HTML_ELEMENT(Summary)
nsGenericHTMLElement*
NS_NewHTMLSummaryElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser)
{
if (!mozilla::dom::HTMLDetailsElement::IsDetailsEnabled()) {
return new mozilla::dom::HTMLUnknownElement(aNodeInfo);
}
return new mozilla::dom::HTMLSummaryElement(aNodeInfo);
}
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {

View File

@ -497,7 +497,7 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
// We need to consider a labels element is moved to another subtree // We need to consider a labels element is moved to another subtree
// with different root, it needs to update labels list and its root // with different root, it needs to update labels list and its root
// as well. // as well.
nsDOMSlots* slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots && slots->mLabelsList) { if (slots && slots->mLabelsList) {
slots->mLabelsList->MaybeResetRoot(SubtreeRoot()); slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
} }
@ -524,7 +524,7 @@ nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
// We need to consider a labels element is removed from tree, // We need to consider a labels element is removed from tree,
// it needs to update labels list and its root as well. // it needs to update labels list and its root as well.
nsDOMSlots* slots = GetExistingDOMSlots(); nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
if (slots && slots->mLabelsList) { if (slots && slots->mLabelsList) {
slots->mLabelsList->MaybeResetRoot(SubtreeRoot()); slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
} }
@ -1730,7 +1730,7 @@ nsGenericHTMLElement::Labels()
{ {
MOZ_ASSERT(IsLabelable(), MOZ_ASSERT(IsLabelable(),
"Labels() only allow labelable elements to use it."); "Labels() only allow labelable elements to use it.");
nsDOMSlots* slots = DOMSlots(); nsExtendedDOMSlots* slots = ExtendedDOMSlots();
if (!slots->mLabelsList) { if (!slots->mLabelsList) {
slots->mLabelsList = new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement, slots->mLabelsList = new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement,

View File

@ -1588,6 +1588,15 @@ protected:
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(_interface, \ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(_interface, \
mNodeInfo->Equals(nsGkAtoms::_tag)) mNodeInfo->Equals(nsGkAtoms::_tag))
namespace mozilla {
namespace dom {
typedef nsGenericHTMLElement* (*HTMLContentCreatorFunction)(
already_AddRefed<mozilla::dom::NodeInfo>&&,
mozilla::dom::FromParser aFromParser);
} // namespace dom
} // namespace mozilla
/** /**
* A macro to declare the NS_NewHTMLXXXElement() functions. * A macro to declare the NS_NewHTMLXXXElement() functions.
@ -1636,6 +1645,13 @@ nsGenericHTMLElement*
NS_NewHTMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, NS_NewHTMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER); mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER);
// Distinct from the above in order to have function pointer that compared unequal
// to a function pointer to the above.
nsGenericHTMLElement*
NS_NewCustomElement(
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER);
NS_DECLARE_NS_NEW_HTML_ELEMENT(Shared) NS_DECLARE_NS_NEW_HTML_ELEMENT(Shared)
NS_DECLARE_NS_NEW_HTML_ELEMENT(SharedList) NS_DECLARE_NS_NEW_HTML_ELEMENT(SharedList)
NS_DECLARE_NS_NEW_HTML_ELEMENT(SharedObject) NS_DECLARE_NS_NEW_HTML_ELEMENT(SharedObject)

View File

@ -84,10 +84,6 @@ using namespace mozilla::dom;
//---------------------------------------------------------------------- //----------------------------------------------------------------------
typedef nsGenericHTMLElement*
(*contentCreatorCallback)(already_AddRefed<mozilla::dom::NodeInfo>&&,
FromParser aFromParser);
nsGenericHTMLElement* nsGenericHTMLElement*
NS_NewHTMLNOTUSEDElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, NS_NewHTMLNOTUSEDElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
FromParser aFromParser) FromParser aFromParser)
@ -96,14 +92,12 @@ NS_NewHTMLNOTUSEDElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
return nullptr; return nullptr;
} }
#define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element, #define HTML_TAG(_tag, _classname, _interfacename) NS_NewHTML##_classname##Element,
#define HTML_HTMLELEMENT_TAG(_tag) NS_NewHTMLElement,
#define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement, #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
static const contentCreatorCallback sContentCreatorCallbacks[] = { static const HTMLContentCreatorFunction sHTMLContentCreatorFunctions[] = {
NS_NewHTMLUnknownElement, NS_NewHTMLUnknownElement,
#include "nsHTMLTagList.h" #include "nsHTMLTagList.h"
#undef HTML_TAG #undef HTML_TAG
#undef HTML_HTMLELEMENT_TAG
#undef HTML_OTHER #undef HTML_OTHER
NS_NewHTMLUnknownElement NS_NewHTMLUnknownElement
}; };
@ -234,9 +228,35 @@ public:
int32_t mStackPos; int32_t mStackPos;
}; };
static void
DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, nsIAtom* aLocalName,
CustomElementConstructor* aConstructor, ErrorResult& aRv)
{
RefPtr<Element> element =
aConstructor->Construct("Custom Element Create", aRv);
if (aRv.Failed()) {
return;
}
if (!element || !element->IsHTMLElement()) {
aRv.ThrowTypeError<MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE>(NS_LITERAL_STRING("HTMLElement"));
return;
}
if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
element->HasChildren() || element->GetAttrCount() ||
element->NodeInfo()->NameAtom() != aLocalName) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
element.forget(aElement);
}
nsresult nsresult
NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
FromParser aFromParser, const nsAString* aIs) FromParser aFromParser, const nsAString* aIs,
mozilla::dom::CustomElementDefinition* aDefinition)
{ {
*aResult = nullptr; *aResult = nullptr;
@ -247,16 +267,109 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
nsIAtom *name = nodeInfo->NameAtom(); nsIAtom *name = nodeInfo->NameAtom();
RefPtr<nsIAtom> tagAtom = nodeInfo->NameAtom();
RefPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : tagAtom;
NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML), NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
"Trying to HTML elements that don't have the XHTML namespace"); "Trying to HTML elements that don't have the XHTML namespace");
int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name); int32_t tag = parserService->HTMLCaseSensitiveAtomTagToId(name);
// Per the Custom Element specification, unknown tags that are valid custom
// element names should be HTMLElement instead of HTMLUnknownElement.
bool isCustomElementName = (tag == eHTMLTag_userdefined && bool isCustomElementName = (tag == eHTMLTag_userdefined &&
nsContentUtils::IsCustomElementName(name)); nsContentUtils::IsCustomElementName(name));
bool isCustomElement = isCustomElementName || aIs;
MOZ_ASSERT_IF(aDefinition, isCustomElement);
// https://dom.spec.whatwg.org/#concept-create-element
// We only handle the "synchronous custom elements flag is set" now.
// For the unset case (e.g. cloning a node), see bug 1319342 for that.
// Step 4.
CustomElementDefinition* definition = aDefinition;
if (CustomElementRegistry::IsCustomElementEnabled() && isCustomElement &&
!definition) {
MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
definition =
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
nodeInfo->NameAtom(),
nodeInfo->NamespaceID(),
typeAtom);
}
// It might be a problem that parser synchronously calls constructor, so filed
// bug 1378079 to figure out what we should do for parser case.
if (definition) {
/*
* Synchronous custom elements flag is determined by 3 places in spec,
* 1) create an element for a token, the flag is determined by
* "will execute script" which is not originally created
* for the HTML fragment parsing algorithm.
* 2) createElement and createElementNS, the flag is the same as
* NOT_FROM_PARSER.
* 3) clone a node, our implementation will not go into this function.
* For the unset case which is non-synchronous only applied for
* inner/outerHTML.
*/
bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT ||
aFromParser == dom::NOT_FROM_PARSER;
// Per discussion in https://github.com/w3c/webcomponents/issues/635,
// use entry global in those places that are called from JS APIs and use the
// node document's global object if it is called from parser.
nsIGlobalObject* global;
if (aFromParser == dom::NOT_FROM_PARSER) {
global = GetEntryGlobal();
} else {
global = nodeInfo->GetDocument()->GetScopeObject();
}
if (!global) {
// In browser chrome code, one may have access to a document which doesn't
// have scope object anymore.
return NS_ERROR_FAILURE;
}
AutoEntryScript aes(global, "create custom elements");
JSContext* cx = aes.cx();
ErrorResult rv;
// Step 5.
if (definition->IsCustomBuiltIn()) {
// SetupCustomElement() should be called with an element that don't have
// CustomElementData setup, if not we will hit the assertion in
// SetCustomElementData().
// Built-in element
*aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
(*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
if (synchronousCustomElements) {
CustomElementRegistry::Upgrade(*aResult, definition, rv);
if (rv.MaybeSetPendingException(cx)) {
aes.ReportException();
}
} else {
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
}
return NS_OK;
}
// Step 6.1.
if (synchronousCustomElements) {
DoCustomElementCreate(aResult, nodeInfo->GetDocument(),
nodeInfo->NameAtom(),
definition->mConstructor, rv);
if (rv.MaybeSetPendingException(cx)) {
NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
}
return NS_OK;
}
// Step 6.2.
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
(*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
return NS_OK;
}
// Per the Custom Element specification, unknown tags that are valid custom
// element names should be HTMLElement instead of HTMLUnknownElement.
if (isCustomElementName) { if (isCustomElementName) {
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser)); NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
} else { } else {
@ -267,8 +380,8 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
if (isCustomElementName || aIs) { if (CustomElementRegistry::IsCustomElementEnabled() && isCustomElement) {
nsContentUtils::SetupCustomElement(*aResult, aIs); (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
} }
return NS_OK; return NS_OK;
@ -283,7 +396,7 @@ CreateHTMLElement(uint32_t aNodeType,
aNodeType == eHTMLTag_userdefined, aNodeType == eHTMLTag_userdefined,
"aNodeType is out of bounds"); "aNodeType is out of bounds");
contentCreatorCallback cb = sContentCreatorCallbacks[aNodeType]; HTMLContentCreatorFunction cb = sHTMLContentCreatorFunctions[aNodeType];
NS_ASSERTION(cb != NS_NewHTMLNOTUSEDElement, NS_ASSERTION(cb != NS_NewHTMLNOTUSEDElement,
"Don't know how to construct tag element!"); "Don't know how to construct tag element!");

View File

@ -1013,26 +1013,6 @@ nsHTMLDocument::SetDomain(const nsAString& aDomain, ErrorResult& rv)
rv = NodePrincipal()->SetDomain(newURI); rv = NodePrincipal()->SetDomain(newURI);
} }
nsGenericHTMLElement*
nsHTMLDocument::GetBody()
{
Element* html = GetHtmlElement();
if (!html) {
return nullptr;
}
for (nsIContent* child = html->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (child->IsHTMLElement(nsGkAtoms::body) ||
child->IsHTMLElement(nsGkAtoms::frameset)) {
return static_cast<nsGenericHTMLElement*>(child);
}
}
return nullptr;
}
NS_IMETHODIMP NS_IMETHODIMP
nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody) nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody)
{ {
@ -1054,31 +1034,6 @@ nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody)
return rv.StealNSResult(); return rv.StealNSResult();
} }
void
nsHTMLDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv)
{
nsCOMPtr<Element> root = GetRootElement();
// The body element must be either a body tag or a frameset tag. And we must
// have a html root tag, otherwise GetBody will not return the newly set
// body.
if (!newBody ||
!newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset) ||
!root || !root->IsHTMLElement() ||
!root->IsHTMLElement(nsGkAtoms::html)) {
rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return;
}
// Use DOM methods so that we pass through the appropriate security checks.
nsCOMPtr<Element> currentBody = GetBodyElement();
if (currentBody) {
root->ReplaceChild(*newBody, *currentBody, rv);
} else {
root->AppendChild(*newBody, rv);
}
}
NS_IMETHODIMP NS_IMETHODIMP
nsHTMLDocument::GetHead(nsIDOMHTMLHeadElement** aHead) nsHTMLDocument::GetHead(nsIDOMHTMLHeadElement** aHead)
{ {
@ -1446,6 +1401,11 @@ nsHTMLDocument::Open(JSContext* cx,
return nullptr; return nullptr;
} }
if (ShouldThrowOnDynamicMarkupInsertion()) {
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
// Set up the content type for insertion // Set up the content type for insertion
nsAutoCString contentType; nsAutoCString contentType;
contentType.AssignLiteral("text/html"); contentType.AssignLiteral("text/html");
@ -1653,6 +1613,11 @@ nsHTMLDocument::Close(ErrorResult& rv)
return; return;
} }
if (ShouldThrowOnDynamicMarkupInsertion()) {
rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (!mParser || !mParser->IsScriptCreated()) { if (!mParser || !mParser->IsScriptCreated()) {
return; return;
} }
@ -1728,6 +1693,10 @@ nsHTMLDocument::WriteCommon(JSContext *cx,
return NS_ERROR_DOM_INVALID_STATE_ERR; return NS_ERROR_DOM_INVALID_STATE_ERR;
} }
if (ShouldThrowOnDynamicMarkupInsertion()) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (mParserAborted) { if (mParserAborted) {
// Hixie says aborting the parser doesn't undefine the insertion point. // Hixie says aborting the parser doesn't undefine the insertion point.
// However, since we null out mParser in that case, we track the // However, since we null out mParser in that case, we track the

View File

@ -175,8 +175,8 @@ public:
JS::MutableHandle<JSObject*> aRetval, JS::MutableHandle<JSObject*> aRetval,
mozilla::ErrorResult& rv); mozilla::ErrorResult& rv);
void GetSupportedNames(nsTArray<nsString>& aNames); void GetSupportedNames(nsTArray<nsString>& aNames);
nsGenericHTMLElement *GetBody(); using nsIDocument::GetBody;
void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv); using nsIDocument::SetBody;
mozilla::dom::HTMLSharedElement *GetHead() { mozilla::dom::HTMLSharedElement *GetHead() {
return static_cast<mozilla::dom::HTMLSharedElement*>(GetHeadElement()); return static_cast<mozilla::dom::HTMLSharedElement*>(GetHeadElement());
} }

View File

@ -46,6 +46,7 @@
#include "nsIWritablePropertyBag2.h" #include "nsIWritablePropertyBag2.h"
#include "nsIContentSecurityPolicy.h" #include "nsIContentSecurityPolicy.h"
#include "nsSandboxFlags.h" #include "nsSandboxFlags.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "nsILoadInfo.h" #include "nsILoadInfo.h"
#include "nsContentSecurityManager.h" #include "nsContentSecurityManager.h"
@ -241,7 +242,7 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
// New script entry point required, due to the "Create a script" step of // New script entry point required, due to the "Create a script" step of
// http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
nsAutoMicroTask mt; mozilla::nsAutoMicroTask mt;
AutoEntryScript aes(innerGlobal, "javascript: URI", true); AutoEntryScript aes(innerGlobal, "javascript: URI", true);
JSContext* cx = aes.cx(); JSContext* cx = aes.cx();
JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject()); JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());

View File

@ -15,31 +15,28 @@ using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
// Hash table that maps nsIAtom* SVG tags to an offset index // Hash table that maps nsIAtom* SVG tags to an offset index
// within the array sContentCreatorCallbacks (offset by TABLE_VALUE_OFFSET) // within the array sSVGContentCreatorFunctions (offset by TABLE_VALUE_OFFSET)
static PLHashTable* sTagAtomTable = nullptr; static PLHashTable* sTagAtomTable = nullptr;
// We don't want to store 0 in the hash table as a return value of 0 from // We don't want to store 0 in the hash table as a return value of 0 from
// PL_HashTableLookupConst indicates that the value is not found // PL_HashTableLookupConst indicates that the value is not found
#define TABLE_VALUE_OFFSET 1 #define TABLE_VALUE_OFFSET 1
#define SVG_TAG(_tag, _classname) \ #define SVG_TAG(_tag, _classname) \
nsresult \ nsresult NS_NewSVG##_classname##Element( \
NS_NewSVG##_classname##Element(nsIContent** aResult, \ nsIContent** aResult, \
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); \ already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); \
\ \
static inline nsresult \ nsresult NS_NewSVG##_classname##Element( \
Create##_classname##Element(nsIContent** aResult, \ nsIContent** aResult, \
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \ already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \
FromParser aFromParser) \ FromParser aFromParser) \
{ \ { \
return NS_NewSVG##_classname##Element(aResult, mozilla::Move(aNodeInfo)); \ return NS_NewSVG##_classname##Element(aResult, mozilla::Move(aNodeInfo)); \
} }
#define SVG_FROM_PARSER_TAG(_tag, _classname)
#define SVG_FROM_PARSER_TAG(_tag, _classname) \
nsresult \
NS_NewSVG##_classname##Element(nsIContent** aResult, \
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \
FromParser aFromParser);
#include "SVGTagList.h" #include "SVGTagList.h"
#undef SVG_TAG #undef SVG_TAG
#undef SVG_FROM_PARSER_TAG #undef SVG_FROM_PARSER_TAG
@ -48,13 +45,8 @@ nsresult
NS_NewSVGElement(Element** aResult, NS_NewSVGElement(Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
typedef nsresult static const SVGContentCreatorFunction sSVGContentCreatorFunctions[] = {
(*contentCreatorCallback)(nsIContent** aResult, #define SVG_TAG(_tag, _classname) NS_NewSVG##_classname##Element,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
FromParser aFromParser);
static const contentCreatorCallback sContentCreatorCallbacks[] = {
#define SVG_TAG(_tag, _classname) Create##_classname##Element,
#define SVG_FROM_PARSER_TAG(_tag, _classname) NS_NewSVG##_classname##Element, #define SVG_FROM_PARSER_TAG(_tag, _classname) NS_NewSVG##_classname##Element,
#include "SVGTagList.h" #include "SVGTagList.h"
#undef SVG_TAG #undef SVG_TAG
@ -124,7 +116,7 @@ NS_NewSVGElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& a
MOZ_CRASH(); MOZ_CRASH();
} }
contentCreatorCallback cb = sContentCreatorCallbacks[index]; SVGContentCreatorFunction cb = sSVGContentCreatorFunctions[index];
nsCOMPtr<nsIContent> content; nsCOMPtr<nsIContent> content;
nsresult rv = cb(getter_AddRefs(content), ni.forget(), aFromParser); nsresult rv = cb(getter_AddRefs(content), ni.forget(), aFromParser);
@ -135,3 +127,15 @@ NS_NewSVGElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& a
// if we don't know what to create, just create a standard svg element: // if we don't know what to create, just create a standard svg element:
return NS_NewSVGElement(aResult, ni.forget()); return NS_NewSVGElement(aResult, ni.forget());
} }
nsresult
NS_NewSVGUnknownElement(nsIContent** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
FromParser aFromParser)
{
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
nsCOMPtr<Element> element;
nsresult rv = NS_NewSVGElement(getter_AddRefs(element), ni.forget());
element.forget(aResult);
return rv;
}

View File

@ -18,7 +18,32 @@ public:
static void Shutdown(); static void Shutdown();
}; };
typedef nsresult (*SVGContentCreatorFunction)(
nsIContent** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser);
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla
#define SVG_TAG(_tag, _classname) \
nsresult NS_NewSVG##_classname##Element( \
nsIContent** aResult, \
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \
mozilla::dom::FromParser aFromParser);
#define SVG_FROM_PARSER_TAG(_tag, _classname) \
nsresult NS_NewSVG##_classname##Element( \
nsIContent** aResult, \
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, \
mozilla::dom::FromParser aFromParser);
#include "SVGTagList.h"
#undef SVG_TAG
#undef SVG_FROM_PARSER_TAG
nsresult
NS_NewSVGUnknownElement(nsIContent** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
mozilla::dom::FromParser aFromParser);
#endif /* mozilla_dom_SVGElementFactory_h */ #endif /* mozilla_dom_SVGElementFactory_h */

View File

@ -9,7 +9,14 @@
This file contains the list of all SVG tags. This file contains the list of all SVG tags.
It is designed to be used as inline input to SVGElementFactory.cpp It is designed to be used as inline input to SVGElementFactory.cpp
*only* through the magic of C preprocessing. through the magic of C preprocessing.
Additionally, it is consumed by the self-regeneration code in
ElementName.java from which nsHtml5ElementName.cpp/h is translated.
See parser/html/java/README.txt.
If you edit this list, you need to re-run ElementName.java
self-regeneration and the HTML parser Java to C++ translation.
All entries must be enclosed in the macro SVG_TAG or SVG_FROM_PARSER_TAG All entries must be enclosed in the macro SVG_TAG or SVG_FROM_PARSER_TAG
which will have cruel and unusual things done to them. which will have cruel and unusual things done to them.

View File

@ -14,6 +14,7 @@ EXPORTS += [
'SVGContentUtils.h', 'SVGContentUtils.h',
'SVGPreserveAspectRatio.h', 'SVGPreserveAspectRatio.h',
'SVGStringList.h', 'SVGStringList.h',
'SVGTagList.h',
] ]
EXPORTS.mozilla.dom += [ EXPORTS.mozilla.dom += [
@ -39,6 +40,7 @@ EXPORTS.mozilla.dom += [
'SVGDefsElement.h', 'SVGDefsElement.h',
'SVGDescElement.h', 'SVGDescElement.h',
'SVGDocument.h', 'SVGDocument.h',
'SVGElementFactory.h',
'SVGEllipseElement.h', 'SVGEllipseElement.h',
'SVGFEBlendElement.h', 'SVGFEBlendElement.h',
'SVGFEColorMatrixElement.h', 'SVGFEColorMatrixElement.h',

View File

@ -0,0 +1,9 @@
[DEFAULT]
support-files =
dummy_page.html
[test_custom_element_htmlconstructor_chrome.html]
skip-if = os == 'android' # bug 1323645
support-files =
htmlconstructor_autonomous_tests.js
htmlconstructor_builtin_tests.js

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Dummy test page</title>
<meta charset="utf-8"/>
</head>
<body>
<p>Dummy test page</p>
</body>
</html>

View File

@ -0,0 +1,81 @@
promises.push(test_with_new_window((testWindow) => {
// Test calling the HTMLElement constructor.
(() => {
SimpleTest.doesThrow(() => {
testWindow.HTMLElement();
}, 'calling the HTMLElement constructor should throw a TypeError');
})();
// Test constructing a HTMLELement.
(() => {
SimpleTest.doesThrow(() => {
new testWindow.HTMLElement();
}, 'constructing a HTMLElement should throw a TypeError');
})();
// Test constructing a custom element with defining HTMLElement as entry.
(() => {
testWindow.customElements.define('x-defining-html-element',
testWindow.HTMLElement);
SimpleTest.doesThrow(() => {
new testWindow.HTMLElement();
}, 'constructing a custom element with defining HTMLElement as registry ' +
'entry should throw a TypeError');
})();
// Test calling a custom element constructor and constructing an autonomous
// custom element.
(() => {
let num_constructor_invocations = 0;
class X extends testWindow.HTMLElement {
constructor() {
super();
num_constructor_invocations++;
}
}
testWindow.customElements.define('x-element', X);
SimpleTest.doesThrow(() => {
X();
}, 'calling an autonomous custom element constructor should throw a TypeError');
let element = new X();
SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(element)), X.prototype,
'constructing an autonomous custom element; ' +
'the element should be a registered constructor');
SimpleTest.is(element.localName, 'x-element',
'constructing an autonomous custom element; ' +
'the element tag name should be "x-element"');
SimpleTest.is(element.namespaceURI, 'http://www.w3.org/1999/xhtml',
'constructing an autonomous custom element; ' +
'the element should be in the HTML namespace');
SimpleTest.is(element.prefix, null,
'constructing an autonomous custom element; ' +
'the element name should not have a prefix');
SimpleTest.is(element.ownerDocument, testWindow.document,
'constructing an autonomous custom element; ' +
'the element should be owned by the registry\'s associated ' +
'document');
SimpleTest.is(num_constructor_invocations, 1,
'constructing an autonomous custom element; ' +
'the constructor should have been invoked once');
})();
// Test if prototype is no an object.
(() => {
function ElementWithNonObjectPrototype() {
let o = Reflect.construct(testWindow.HTMLElement, [], new.target);
SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(o)), window.HTMLElement.prototype,
'constructing an autonomous custom element; ' +
'if prototype is not object, fallback from NewTarget\'s realm');
}
// Prototype have to be an object during define(), otherwise define will
// throw an TypeError exception.
ElementWithNonObjectPrototype.prototype = {};
testWindow.customElements.define('x-non-object-prototype',
ElementWithNonObjectPrototype);
ElementWithNonObjectPrototype.prototype = "string";
new ElementWithNonObjectPrototype();
})();
}));

View File

@ -0,0 +1,247 @@
[
// [TagName, InterfaceName]
['a', 'Anchor'],
['abbr', ''],
['acronym', ''],
['address', ''],
['area', 'Area'],
['article', ''],
['aside', ''],
['audio', 'Audio'],
['b', ''],
['base', 'Base'],
['basefont', ''],
['bdo', ''],
['big', ''],
['blockquote', 'Quote'],
['body', 'Body'],
['br', 'BR'],
['button', 'Button'],
['canvas', 'Canvas'],
['caption', 'TableCaption'],
['center', ''],
['cite', ''],
['code', ''],
['col', 'TableCol'],
['colgroup', 'TableCol'],
['data', 'Data'],
['datalist', 'DataList'],
['dd', ''],
['del', 'Mod'],
['details', 'Details'],
['dfn', ''],
['dir', 'Directory'],
['div', 'Div'],
['dl', 'DList'],
['dt', ''],
['em', ''],
['embed', 'Embed'],
['fieldset', 'FieldSet'],
['figcaption', ''],
['figure', ''],
['font', 'Font'],
['footer', ''],
['form', 'Form'],
['frame', 'Frame'],
['frameset', 'FrameSet'],
['h1', 'Heading'],
['h2', 'Heading'],
['h3', 'Heading'],
['h4', 'Heading'],
['h5', 'Heading'],
['h6', 'Heading'],
['head', 'Head'],
['header', ''],
['hgroup', ''],
['hr', 'HR'],
['html', 'Html'],
['i', ''],
['iframe', 'IFrame'],
['image', ''],
['img', 'Image'],
['input', 'Input'],
['ins', 'Mod'],
['kbd', ''],
['label', 'Label'],
['legend', 'Legend'],
['li', 'LI'],
['link', 'Link'],
['listing', 'Pre'],
['main', ''],
['map', 'Map'],
['mark', ''],
['marquee', 'Div'],
['menu', 'Menu'],
['menuitem', 'MenuItem'],
['meta', 'Meta'],
['meter', 'Meter'],
['nav', ''],
['nobr', ''],
['noembed', ''],
['noframes', ''],
['noscript', ''],
['object', 'Object'],
['ol', 'OList'],
['optgroup', 'OptGroup'],
['option', 'Option'],
['output', 'Output'],
['p', 'Paragraph'],
['param', 'Param'],
['picture', 'Picture'],
['plaintext', ''],
['pre', 'Pre'],
['progress', 'Progress'],
['q', 'Quote'],
['rb', ''],
['rp', ''],
['rt', ''],
['rtc', ''],
['ruby', ''],
['s', ''],
['samp', ''],
['script', 'Script'],
['section', ''],
['select', 'Select'],
['small', ''],
['source', 'Source'],
['span', 'Span'],
['strike', ''],
['strong', ''],
['style', 'Style'],
['sub', ''],
['summary', ''],
['sup', ''],
['table', 'Table'],
['tbody', 'TableSection'],
['td', 'TableCell'],
['textarea', 'TextArea'],
['tfoot', 'TableSection'],
['th', 'TableCell'],
['thead', 'TableSection'],
['template', 'Template'],
['time', 'Time'],
['title', 'Title'],
['tr', 'TableRow'],
['track', 'Track'],
['tt', ''],
['u', ''],
['ul', 'UList'],
['var', ''],
['video', 'Video'],
['wbr', ''],
['xmp', 'Pre'],
].forEach((e) => {
let tagName = e[0];
let interfaceName = 'HTML' + e[1] + 'Element';
promises.push(test_with_new_window((testWindow) => {
// Use window from iframe to isolate the test.
// Test calling the HTML*Element constructor.
(() => {
SimpleTest.doesThrow(() => {
testWindow[interfaceName]();
}, 'calling the ' + interfaceName + ' constructor should throw a TypeError');
})();
// Test constructing a HTML*ELement.
(() => {
SimpleTest.doesThrow(() => {
new testWindow[interfaceName]();
}, 'constructing a ' + interfaceName + ' should throw a TypeError');
})();
// Test constructing a custom element with defining HTML*Element as entry.
(() => {
testWindow.customElements.define('x-defining-' + tagName,
testWindow[interfaceName]);
SimpleTest.doesThrow(() => {
new testWindow[interfaceName]();
}, 'constructing a custom element with defining ' + interfaceName +
' as registry entry should throw a TypeError');
})();
// Since HTMLElement can be registered without specifying "extends", skip
// testing HTMLElement tags.
if (interfaceName !== "HTMLElement") {
// Test constructing a customized HTML*Element with defining a registry entry
// without specifying "extends".
(() => {
class X extends testWindow[interfaceName] {}
testWindow.customElements.define('x-defining-invalid-' + tagName, X);
SimpleTest.doesThrow(() => {
new X();
}, 'constructing a customized ' + interfaceName + ' with defining a ' +
'registry entry without specifying "extends" should throw a TypeError');
})();
}
// Test constructing a built-in custom element with defining a registry entry
// with incorrect "extends" information.
(() => {
class X extends testWindow[interfaceName] {}
testWindow.customElements.define('x-defining-incorrect-' + tagName, X,
{ extends: tagName === 'img' ? 'p' : 'img' });
SimpleTest.doesThrow(() => {
new X();
}, 'constructing a customized ' + interfaceName + ' with defining a ' +
'registry entry with incorrect "extends" should throw a TypeError');
})();
// Test calling a custom element constructor and constructing a built-in
// custom element.
(() => {
let num_constructor_invocations = 0;
class X extends testWindow[interfaceName] {
constructor() {
super();
num_constructor_invocations++;
}
}
testWindow.customElements.define('x-' + tagName, X, { extends: tagName });
SimpleTest.doesThrow(() => {
X();
}, 'calling a customized ' + interfaceName + ' constructor should throw a TypeError');
let element = new X();
SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(element)), X.prototype,
'constructing a customized ' + interfaceName +
'; the element should be a registered constructor');
SimpleTest.is(element.localName, tagName,
'constructing a customized ' + interfaceName +
'; the element tag name should be "' + tagName + '"');
SimpleTest.is(element.namespaceURI, 'http://www.w3.org/1999/xhtml',
'constructing a customized ' + interfaceName +
'; the element should be in the HTML namespace');
SimpleTest.is(element.prefix, null,
'constructing a customized ' + interfaceName +
'; the element name should not have a prefix');
SimpleTest.is(element.ownerDocument, testWindow.document,
'constructing a customized ' + interfaceName +
'; the element should be owned by the registry\'s associated ' +
'document');
SimpleTest.is(num_constructor_invocations, 1,
'constructing a customized ' + interfaceName +
'; the constructor should have been invoked once');
})();
// Test if prototype is no an object.
(() => {
function ElementWithNonObjectPrototype() {
let o = Reflect.construct(testWindow[interfaceName], [], new.target);
SimpleTest.is(Object.getPrototypeOf(Cu.waiveXrays(o)), window[interfaceName].prototype,
'constructing a customized ' + interfaceName +
'; if prototype is not object, fallback from NewTarget\'s realm');
}
// Prototype have to be an object during define(), otherwise define will
// throw an TypeError exception.
ElementWithNonObjectPrototype.prototype = {};
testWindow.customElements.define('x-non-object-prototype-' + tagName,
ElementWithNonObjectPrototype,
{ extends: tagName });
ElementWithNonObjectPrototype.prototype = "string";
new ElementWithNonObjectPrototype();
})();
}));
});

View File

@ -1,21 +1,28 @@
[DEFAULT] [DEFAULT]
support-files = support-files =
inert_style.css inert_style.css
dummy_page.html
[test_bug900724.html] [test_bug900724.html]
[test_bug1017896.html] [test_bug1017896.html]
[test_bug1176757.html] [test_bug1176757.html]
[test_bug1276240.html] [test_bug1276240.html]
[test_content_element.html] [test_content_element.html]
[test_custom_element_adopt_callbacks.html]
[test_custom_element_callback_innerhtml.html] [test_custom_element_callback_innerhtml.html]
[test_custom_element_clone_callbacks.html] skip-if = true # disabled - See bug 1390396
[test_custom_element_clone_callbacks_extended.html] [test_custom_element_htmlconstructor.html]
[test_custom_element_import_node_created_callback.html] skip-if = os == 'android' # bug 1323645
support-files =
htmlconstructor_autonomous_tests.js
htmlconstructor_builtin_tests.js
[test_custom_element_in_shadow.html] [test_custom_element_in_shadow.html]
skip-if = true || stylo # disabled - See bug 1390396 and 1293844
[test_custom_element_register_invalid_callbacks.html] [test_custom_element_register_invalid_callbacks.html]
[test_custom_element_throw_on_dynamic_markup_insertion.html]
[test_custom_element_get.html] [test_custom_element_get.html]
[test_custom_element_when_defined.html] [test_custom_element_when_defined.html]
[test_custom_element_uncatchable_exception.html]
skip-if = !debug # TestFunctions only applied in debug builds
[test_nested_content_element.html] [test_nested_content_element.html]
[test_dest_insertion_points.html] [test_dest_insertion_points.html]
[test_dest_insertion_points_shadow.html] [test_dest_insertion_points_shadow.html]
@ -25,10 +32,11 @@ support-files =
[test_document_adoptnode.html] [test_document_adoptnode.html]
[test_document_importnode.html] [test_document_importnode.html]
[test_document_register.html] [test_document_register.html]
[test_document_register_base_queue.html]
[test_document_register_lifecycle.html] [test_document_register_lifecycle.html]
skip-if = true # disabled - See bug 1390396
[test_document_register_parser.html] [test_document_register_parser.html]
[test_document_register_stack.html] [test_document_register_stack.html]
skip-if = true # disabled - See bug 1390396
[test_document_shared_registry.html] [test_document_shared_registry.html]
[test_event_dispatch.html] [test_event_dispatch.html]
[test_event_retarget.html] [test_event_retarget.html]

View File

@ -47,7 +47,7 @@ test();
// test with webcomponents disabled // test with webcomponents disabled
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv( SpecialPowers.pushPrefEnv(
{ 'set': [["dom.webcomponents.enabled", false]]}, runTest); { 'set': [["dom.webcomponents.customelements.enabled", false]]}, runTest);
</script> </script>
</pre> </pre>

View File

@ -1,29 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
-->
<head>
<title>Test callbacks for adopted custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<template id="template"><x-foo></x-foo></template>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
<script>
var p = Object.create(HTMLElement.prototype);
p.createdCallback = function() {
ok(false, "Created callback should not be called for adopted node.");
};
document.registerElement("x-foo", { prototype: p });
var template = document.getElementById("template");
var adoptedFoo = document.adoptNode(template.content.firstChild);
is(adoptedFoo.nodeName, "X-FOO");
</script>
</body>
</html>

View File

@ -16,7 +16,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1102502
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
var attachedCallbackCount = 0; var connectedCallbackCount = 0;
var p = Object.create(HTMLElement.prototype); var p = Object.create(HTMLElement.prototype);
@ -24,12 +24,12 @@ p.createdCallback = function() {
ok(true, "createdCallback called."); ok(true, "createdCallback called.");
}; };
p.attachedCallback = function() { p.connectedCallback = function() {
ok(true, "attachedCallback should be called when the parser creates an element in the document."); ok(true, "connectedCallback should be called when the parser creates an element in the document.");
attachedCallbackCount++; connectedCallbackCount++;
// attachedCallback should be called twice, once for the element created for innerHTML and // connectedCallback should be called twice, once for the element created for innerHTML and
// once for the element created in this document. // once for the element created in this document.
if (attachedCallbackCount == 2) { if (connectedCallbackCount == 2) {
SimpleTest.finish(); SimpleTest.finish();
} }
} }

View File

@ -1,54 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
-->
<head>
<title>Test callbacks for cloned custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
<script>
SimpleTest.waitForExplicitFinish();
// Test to make sure created callback is called on clones that are upgraded and clones
// created after registering the custom element.
var callbackCalledOnUpgrade = false;
var callbackCalledOnClone = false;
var foo = document.createElement("x-foo");
var fooClone = foo.cloneNode(true);
var p = Object.create(HTMLElement.prototype);
p.createdCallback = function() {
is(this.__proto__, p, "Correct prototype should be set on custom elements.");
if (this == fooClone) {
// Callback called for the element created before registering the custom element.
// Should be called on element upgrade.
is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
callbackCalledOnUpgrade = true;
} else if (this != foo) {
// Callback called for the element created after registering the custom element.
is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
callbackCalledOnClone = true;
}
if (callbackCalledOnUpgrade && callbackCalledOnClone) {
SimpleTest.finish();
}
};
document.registerElement("x-foo", { prototype: p });
var anotherFooClone = foo.cloneNode(true);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -1,40 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
-->
<head>
<title>Test callbacks for cloned extended custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
<script>
SimpleTest.waitForExplicitFinish();
// Test to make sure created callback is called on clones created after
// registering the custom element.
var count = 0;
var p = Object.create(HTMLButtonElement.prototype);
p.createdCallback = function() { // should be called by createElement and cloneNode
is(this.__proto__, p, "Correct prototype should be set on custom elements.");
if (++count == 2) {
SimpleTest.finish();
}
};
document.registerElement("x-foo", { prototype: p, extends: "button" });
var foo = document.createElement("button", {is: "x-foo"});
is(foo.getAttribute("is"), "x-foo");
var fooClone = foo.cloneNode(true);
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1274159
-->
<head>
<title>Test HTMLConstructor for custom elements.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1274159">Bug 1274159</a>
<script type="text/javascript">
function test_with_new_window(f) {
return new Promise((aResolve) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('type', 'content');
iframe.setAttribute('src', 'dummy_page.html');
iframe.onload = function() {
f(iframe.contentWindow);
aResolve();
};
document.body.appendChild(iframe);
});
}
// Fake the Cu.waiveXrays, so that we can share the tests with mochitest chrome.
var Cu = { waiveXrays: (obj) => obj };
var promises = [];
SimpleTest.waitForExplicitFinish();
</script>
<!-- Test cases for autonomous element -->
<script type="text/javascript" src="htmlconstructor_autonomous_tests.js"></script>
<!-- Test cases for customized built-in element -->
<script type="text/javascript" src="htmlconstructor_builtin_tests.js"></script>
<script type="text/javascript">
Promise.all(promises).then(() => {
SimpleTest.finish();
});
</script>
</body>
</html>

View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1274159
-->
<head>
<title>Test HTMLConstructor for custom elements.</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1274159">Bug 1274159</a>
<script type="text/javascript">
function test_with_new_window(f) {
return new Promise((aResolve) => {
let iframe = document.createElement('iframe');
iframe.setAttribute('type', 'content');
iframe.setAttribute('src', 'http://example.org/tests/dom/tests/mochitest/webcomponents/dummy_page.html');
iframe.onload = function() {
f(iframe.contentWindow);
aResolve();
};
document.body.appendChild(iframe);
});
}
var Cu = Components.utils;
var promises = [];
SimpleTest.waitForExplicitFinish();
</script>
<!-- Test cases for autonomous element -->
<script type="text/javascript" src="htmlconstructor_autonomous_tests.js"></script>
<!-- Test cases for customized built-in element -->
<script type="text/javascript" src="htmlconstructor_builtin_tests.js"></script>
<script type="text/javascript">
Promise.all(promises).then(() => {
SimpleTest.finish();
});
</script>
</body>
</html>

View File

@ -1,34 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1093680
-->
<head>
<title>Test created callback order for imported custom element.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<template id="template"><x-foo><span></span></x-foo></template>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1093680">Bug 1093680</a>
<script>
var fooProtoCreatedCallbackCalled = false;
var fooProto = Object.create(HTMLElement.prototype);
fooProto.createdCallback = function() {
ok(this.firstElementChild, "When the created callback is called, the element should already have a child because the callback should only be called after cloning all the contents.");
fooProtoCreatedCallbackCalled = true;
};
document.registerElement("x-foo", { prototype: fooProto });
var template = document.getElementById("template");
// Importing node will implicityly clone the conent in the main document.
var adoptedFoo = document.importNode(template.content, true);
ok(fooProtoCreatedCallbackCalled, "Created callback should be called after importing custom element into document");
</script>
</body>
</html>

View File

@ -19,9 +19,6 @@ const testWindow = iframe.contentDocument.defaultView;
// This is for backward compatibility. // This is for backward compatibility.
// We should do the same checks for the callbacks from v0 spec. // We should do the same checks for the callbacks from v0 spec.
[ [
'createdCallback',
'attachedCallback',
'detachedCallback',
'attributeChangedCallback', 'attributeChangedCallback',
].forEach(callback => { ].forEach(callback => {
var c = class {}; var c = class {};

View File

@ -0,0 +1,66 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1378079
-->
<head>
<title>Test throw on dynamic markup insertion when creating element synchronously from parser</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1378079">Bug 1378079</a>
<div id="container"></div>
<script>
class DoDocumentOpenInCtor extends HTMLElement {
constructor() {
super();
document.open();
}
};
customElements.define('x-document-open-in-ctor', DoDocumentOpenInCtor);
class DoDocumentWriteInCtor extends HTMLElement {
constructor() {
super();
document.write('<div>This should not be shown</div>');
}
};
customElements.define('x-document-write-in-ctor', DoDocumentWriteInCtor);
class DoDocumentCloseInCtor extends HTMLElement {
constructor() {
super();
document.close();
}
};
customElements.define('x-document-close-in-ctor', DoDocumentCloseInCtor);
window.errors = [];
window.onerror = function(message, url, lineNumber, columnNumber, error) {
errors.push(error.name);
return true;
}
var expectedErrorCount = 0;
document.write("<x-document-open-in-ctor></x-document-open-in-ctor>");
expectedErrorCount++;
is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
document.write("<x-document-write-in-ctor></x-document-write-in-ctor>");
expectedErrorCount++;
is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
document.write("<x-document-close-in-ctor></x-document-close-in-ctor>");
expectedErrorCount++;
is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
</script>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1407669
-->
<head>
<title>Test custom elements runtime exception</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1407669">Bug 1407669</a>
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
window.onerror = function (e) {
ok(false, "How did we get here!?");
}
class Foo extends HTMLElement {
constructor() {
super()
TestFunctions.throwUncatchableException();
}
}
customElements.define("test-custom-element", Foo);
let element = document.createElement("test-custom-element");
is(element instanceof HTMLUnknownElement, true, "It should be a HTMLUnknownElement when uncatchable exception throws in constructor");
ok(true, "Uncatchable exception should not report");
SimpleTest.finish();
});
</script>
</body>
</html>

View File

@ -103,52 +103,12 @@ function startTest() {
is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type."); is(extendedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
is(extendedButton.type, "submit", "Created element should be a button with type of \"submit\""); is(extendedButton.type, "submit", "Created element should be a button with type of \"submit\"");
// document.createElementNS with different namespace than definition.
try {
var svgButton = document.createElementNS("http://www.w3.org/2000/svg", "button", {is: "x-extended-button"});
ok(false, "An exception should've been thrown");
} catch(err) {
is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
}
// document.createElementNS with no namespace.
try {
var noNamespaceButton = document.createElementNS("", "button", {is: "x-extended-button"});
ok(false, "An exception should've been thrown");
} catch(err) {
is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
}
// document.createElement with non-existant extended type.
try {
var normalButton = document.createElement("button", {is: "x-non-existant"});
ok(false, "An exception should've been thrown");
} catch(err) {
is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
}
// document.createElement with exteneded type that does not match with local name of element.
try {
var normalDiv = document.createElement("div", {is: "x-extended-button"});
ok(false, "An exception should've been thrown");
} catch(err) {
is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
}
// Custom element constructor. // Custom element constructor.
var constructedButton = new buttonConstructor(); var constructedButton = new buttonConstructor();
is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON"); is(constructedButton.tagName, "BUTTON", "Created element should have local name of BUTTON");
is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type."); is(constructedButton.__proto__, extendedProto, "Created element should have the prototype of the extended type.");
is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type."); is(constructedButton.getAttribute("is"), "x-extended-button", "The |is| attribute of the created element should be the extended type.");
// document.createElement with different namespace than definition for extended element.
try {
var htmlText = document.createElement("text", {is: "x-extended-text"});
ok(false, "An exception should've been thrown");
} catch(err) {
is(err.name, "NotFoundError", "A NotFoundError exception should've been thrown");
}
// Try creating an element with a custom element name, but not in the html namespace. // Try creating an element with a custom element name, but not in the html namespace.
var htmlNamespaceProto = Object.create(HTMLElement.prototype); var htmlNamespaceProto = Object.create(HTMLElement.prototype);
document.registerElement("x-in-html-namespace", { prototype: htmlNamespaceProto }); document.registerElement("x-in-html-namespace", { prototype: htmlNamespaceProto });

View File

@ -1,48 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=783129
-->
<head>
<title>Test for document.registerElement lifecycle callback</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
var p = Object.create(HTMLElement.prototype);
var createdCallbackCallCount = 0;
// By the time the base element queue is processed via the microtask,
// both x-hello elements should be in the document.
p.createdCallback = function() {
var one = document.getElementById("one");
var two = document.getElementById("two");
isnot(one, null, "First x-hello element should be in the tree.");
isnot(two, null, "Second x-hello element should be in the tree.");
createdCallbackCallCount++;
if (createdCallbackCallCount == 2) {
SimpleTest.finish();
}
if (createdCallbackCallCount > 2) {
ok(false, "Created callback called too much.");
}
};
p.attributeChangedCallback = function(name, oldValue, newValue) {
ok(false, "Attribute changed callback should not be called because callbacks should not be queued until created callback invoked.");
};
document.registerElement("x-hello", { prototype: p });
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
<x-hello id="one"></x-hello>
<x-hello id="two"></x-hello>
<script>
</script>
</body>
</html>

View File

@ -11,7 +11,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783129
var extendedButtonProto = Object.create(HTMLButtonElement.prototype); var extendedButtonProto = Object.create(HTMLButtonElement.prototype);
var buttonCallbackCalled = false; var buttonCallbackCalled = false;
extendedButtonProto.createdCallback = function() { extendedButtonProto.connectedCallback = function() {
is(buttonCallbackCalled, false, "created callback for x-button should only be called once."); is(buttonCallbackCalled, false, "created callback for x-button should only be called once.");
is(this.tagName, "BUTTON", "Only the <button> element should be upgraded."); is(this.tagName, "BUTTON", "Only the <button> element should be upgraded.");
buttonCallbackCalled = true; buttonCallbackCalled = true;
@ -21,7 +21,7 @@ document.registerElement("x-button", { prototype: extendedButtonProto, extends:
var divProto = Object.create(HTMLDivElement.prototype); var divProto = Object.create(HTMLDivElement.prototype);
var divCallbackCalled = false; var divCallbackCalled = false;
divProto.createdCallback = function() { divProto.connectedCallback = function() {
is(divCallbackCalled, false, "created callback for x-div should only be called once."); is(divCallbackCalled, false, "created callback for x-div should only be called once.");
is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div."); is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div.");
is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded."); is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded.");

View File

@ -28,7 +28,8 @@ function testChangeAttributeInCreatedCallback() {
createdCallbackCalled = true; createdCallbackCalled = true;
is(attributeChangedCallbackCalled, false, "Attribute changed callback should not have been called prior to setting the attribute."); is(attributeChangedCallbackCalled, false, "Attribute changed callback should not have been called prior to setting the attribute.");
this.setAttribute("foo", "bar"); this.setAttribute("foo", "bar");
is(attributeChangedCallbackCalled, false, "While element is being created, element should not be added to the current element callback queue."); is(attributeChangedCallbackCalled, true, "While element is being created, element should be added to the current element callback queue.");
runNextTest();
}; };
p.attributeChangedCallback = function(name, oldValue, newValue) { p.attributeChangedCallback = function(name, oldValue, newValue) {
@ -36,7 +37,6 @@ function testChangeAttributeInCreatedCallback() {
is(attributeChangedCallbackCalled, false, "attributeChanged callback should only be called once in this tests."); is(attributeChangedCallbackCalled, false, "attributeChanged callback should only be called once in this tests.");
is(newValue, "bar", "The new value should be 'bar'"); is(newValue, "bar", "The new value should be 'bar'");
attributeChangedCallbackCalled = true; attributeChangedCallbackCalled = true;
runNextTest();
}; };
document.registerElement("x-one", { prototype: p }); document.registerElement("x-one", { prototype: p });

View File

@ -14,37 +14,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783129
<script> <script>
var container = document.getElementById("container"); var container = document.getElementById("container");
function createdCallbackFromMainDoc() {
var createdCallbackCalled = false;
var assocDoc = document.implementation.createHTMLDocument();
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
is(createdCallbackCalled, false, "created callback should only be called once in this tests.");
createdCallbackCalled = true;
runNextTest();
};
assocDoc.registerElement("x-associated-doc-callback-elem", { prototype: proto });
document.createElement("x-associated-doc-callback-elem");
}
function createdCallbackFromDocHTMLNamespace() {
var createdCallbackCalled = false;
var assocDoc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", null);
var somediv = assocDoc.createElement("div");
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
is(createdCallbackCalled, false, "created callback should only be called once in this tests.");
createdCallbackCalled = true;
runNextTest();
};
assocDoc.registerElement("x-assoc-doc-with-ns-callback-elem", { prototype: proto });
document.createElement("x-assoc-doc-with-ns-callback-elem");
}
function registerNoRegistryDoc() { function registerNoRegistryDoc() {
var assocDoc = document.implementation.createDocument(null, "html"); var assocDoc = document.implementation.createDocument(null, "html");
try { try {
@ -65,8 +34,6 @@ function runNextTest() {
} }
var testFunctions = [ var testFunctions = [
createdCallbackFromMainDoc,
createdCallbackFromDocHTMLNamespace,
registerNoRegistryDoc, registerNoRegistryDoc,
SimpleTest.finish SimpleTest.finish
]; ];

View File

@ -37,6 +37,7 @@ MOCHITEST_CHROME_MANIFESTS += [
'mochitest/geolocation/chrome.ini', 'mochitest/geolocation/chrome.ini',
'mochitest/localstorage/chrome.ini', 'mochitest/localstorage/chrome.ini',
'mochitest/sessionstorage/chrome.ini', 'mochitest/sessionstorage/chrome.ini',
'mochitest/webcomponents/chrome.ini',
'mochitest/whatwg/chrome.ini', 'mochitest/whatwg/chrome.ini',
] ]

View File

@ -12,7 +12,7 @@
interface Attr : Node { interface Attr : Node {
readonly attribute DOMString localName; readonly attribute DOMString localName;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString value; attribute DOMString value;
[Constant] [Constant]

View File

@ -10,7 +10,7 @@
interface CSSRule; interface CSSRule;
interface CSSStyleDeclaration { interface CSSStyleDeclaration {
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString cssText; attribute DOMString cssText;
readonly attribute unsigned long length; readonly attribute unsigned long length;
@ -22,9 +22,9 @@ interface CSSStyleDeclaration {
[Throws] [Throws]
CSSValue? getPropertyCSSValue(DOMString property); CSSValue? getPropertyCSSValue(DOMString property);
DOMString getPropertyPriority(DOMString property); DOMString getPropertyPriority(DOMString property);
[Throws] [CEReactions, Throws]
void setProperty(DOMString property, [TreatNullAs=EmptyString] DOMString value, [TreatNullAs=EmptyString] optional DOMString priority = ""); void setProperty(DOMString property, [TreatNullAs=EmptyString] DOMString value, [TreatNullAs=EmptyString] optional DOMString priority = "");
[Throws] [CEReactions, Throws]
DOMString removeProperty(DOMString property); DOMString removeProperty(DOMString property);
readonly attribute CSSRule? parentRule; readonly attribute CSSRule? parentRule;

View File

@ -9,13 +9,13 @@
[NoInterfaceObject] [NoInterfaceObject]
interface ChildNode { interface ChildNode {
[Throws, Unscopable] [CEReactions, Throws, Unscopable]
void before((Node or DOMString)... nodes); void before((Node or DOMString)... nodes);
[Throws, Unscopable] [CEReactions, Throws, Unscopable]
void after((Node or DOMString)... nodes); void after((Node or DOMString)... nodes);
[Throws, Unscopable] [CEReactions, Throws, Unscopable]
void replaceWith((Node or DOMString)... nodes); void replaceWith((Node or DOMString)... nodes);
[Unscopable] [CEReactions, Unscopable]
void remove(); void remove();
}; };

View File

@ -5,7 +5,7 @@
// https://html.spec.whatwg.org/#dom-window-customelements // https://html.spec.whatwg.org/#dom-window-customelements
[Func="CustomElementRegistry::IsCustomElementEnabled"] [Func="CustomElementRegistry::IsCustomElementEnabled"]
interface CustomElementRegistry { interface CustomElementRegistry {
[Throws] [CEReactions, Throws]
void define(DOMString name, Function functionConstructor, void define(DOMString name, Function functionConstructor,
optional ElementDefinitionOptions options); optional ElementDefinitionOptions options);
any get(DOMString name); any get(DOMString name);

View File

@ -14,7 +14,8 @@
[OverrideBuiltins] [OverrideBuiltins]
interface DOMStringMap { interface DOMStringMap {
getter DOMString (DOMString name); getter DOMString (DOMString name);
[Throws] [CEReactions, Throws]
setter creator void (DOMString name, DOMString value); setter creator void (DOMString name, DOMString value);
[CEReactions]
deleter void (DOMString name); deleter void (DOMString name);
}; };

View File

@ -14,17 +14,17 @@ interface DOMTokenList {
readonly attribute unsigned long length; readonly attribute unsigned long length;
getter DOMString? item(unsigned long index); getter DOMString? item(unsigned long index);
boolean contains(DOMString token); boolean contains(DOMString token);
[Throws] [CEReactions, Throws]
void add(DOMString... tokens); void add(DOMString... tokens);
[Throws] [CEReactions, Throws]
void remove(DOMString... tokens); void remove(DOMString... tokens);
[Throws] [CEReactions, Throws]
void replace(DOMString token, DOMString newToken); void replace(DOMString token, DOMString newToken);
[Throws] [CEReactions, Throws]
boolean toggle(DOMString token, optional boolean force); boolean toggle(DOMString token, optional boolean force);
[Throws] [CEReactions, Throws]
boolean supports(DOMString token); boolean supports(DOMString token);
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString value; attribute DOMString value;
stringifier DOMString (); stringifier DOMString ();
iterable<DOMString?>; iterable<DOMString?>;

View File

@ -65,9 +65,9 @@ interface Document : Node {
[NewObject, Throws] [NewObject, Throws]
ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
[Throws] [CEReactions, Throws]
Node importNode(Node node, optional boolean deep = false); Node importNode(Node node, optional boolean deep = false);
[Throws] [CEReactions, Throws]
Node adoptNode(Node node); Node adoptNode(Node node);
[NewObject, Throws] [NewObject, Throws]
@ -108,9 +108,9 @@ partial interface Document {
// DOM tree accessors // DOM tree accessors
//(Not proxy yet)getter object (DOMString name); //(Not proxy yet)getter object (DOMString name);
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString title; attribute DOMString title;
[Pure] [CEReactions, Pure]
attribute DOMString dir; attribute DOMString dir;
//(HTML only) attribute HTMLElement? body; //(HTML only) attribute HTMLElement? body;
//(HTML only)readonly attribute HTMLHeadElement? head; //(HTML only)readonly attribute HTMLHeadElement? head;

View File

@ -25,9 +25,9 @@ interface Element : Node {
[Pure] [Pure]
readonly attribute DOMString tagName; readonly attribute DOMString tagName;
[Pure] [CEReactions, Pure]
attribute DOMString id; attribute DOMString id;
[Pure] [CEReactions, Pure]
attribute DOMString className; attribute DOMString className;
[Constant, PutForwards=value] [Constant, PutForwards=value]
readonly attribute DOMTokenList classList; readonly attribute DOMTokenList classList;
@ -40,15 +40,15 @@ interface Element : Node {
DOMString? getAttribute(DOMString name); DOMString? getAttribute(DOMString name);
[Pure] [Pure]
DOMString? getAttributeNS(DOMString? namespace, DOMString localName); DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
[Throws] [CEReactions, Throws]
boolean toggleAttribute(DOMString name, optional boolean force); boolean toggleAttribute(DOMString name, optional boolean force);
[Throws] [CEReactions, Throws]
void setAttribute(DOMString name, DOMString value); void setAttribute(DOMString name, DOMString value);
[Throws] [CEReactions, Throws]
void setAttributeNS(DOMString? namespace, DOMString name, DOMString value); void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);
[Throws] [CEReactions, Throws]
void removeAttribute(DOMString name); void removeAttribute(DOMString name);
[Throws] [CEReactions, Throws]
void removeAttributeNS(DOMString? namespace, DOMString localName); void removeAttributeNS(DOMString? namespace, DOMString localName);
[Pure] [Pure]
boolean hasAttribute(DOMString name); boolean hasAttribute(DOMString name);
@ -72,7 +72,7 @@ interface Element : Node {
[Pure] [Pure]
HTMLCollection getElementsByClassName(DOMString classNames); HTMLCollection getElementsByClassName(DOMString classNames);
[Throws, Pure] [CEReactions, Throws, Pure]
Element? insertAdjacentElement(DOMString where, Element element); // historical Element? insertAdjacentElement(DOMString where, Element element); // historical
[Throws] [Throws]
@ -137,12 +137,12 @@ interface Element : Node {
// Obsolete methods. // Obsolete methods.
Attr? getAttributeNode(DOMString name); Attr? getAttributeNode(DOMString name);
[Throws] [CEReactions, Throws]
Attr? setAttributeNode(Attr newAttr); Attr? setAttributeNode(Attr newAttr);
[Throws] [CEReactions, Throws]
Attr? removeAttributeNode(Attr oldAttr); Attr? removeAttributeNode(Attr oldAttr);
Attr? getAttributeNodeNS(DOMString? namespaceURI, DOMString localName); Attr? getAttributeNodeNS(DOMString? namespaceURI, DOMString localName);
[Throws] [CEReactions, Throws]
Attr? setAttributeNodeNS(Attr newAttr); Attr? setAttributeNodeNS(Attr newAttr);
[ChromeOnly] [ChromeOnly]
@ -212,11 +212,11 @@ partial interface Element {
// http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface // http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface
partial interface Element { partial interface Element {
[Pure,SetterThrows,TreatNullAs=EmptyString] [CEReactions, Pure,SetterThrows,TreatNullAs=EmptyString]
attribute DOMString innerHTML; attribute DOMString innerHTML;
[Pure,SetterThrows,TreatNullAs=EmptyString] [CEReactions, Pure,SetterThrows,TreatNullAs=EmptyString]
attribute DOMString outerHTML; attribute DOMString outerHTML;
[Throws] [CEReactions, Throws]
void insertAdjacentHTML(DOMString position, DOMString text); void insertAdjacentHTML(DOMString position, DOMString text);
}; };

View File

@ -94,7 +94,6 @@ interface GlobalEventHandlers {
[Pref="dom.select_events.enabled"] [Pref="dom.select_events.enabled"]
attribute EventHandler onselectstart; attribute EventHandler onselectstart;
[Pref="dom.details_element.enabled"]
attribute EventHandler ontoggle; attribute EventHandler ontoggle;
// Pointer events handlers // Pointer events handlers

View File

@ -12,25 +12,26 @@
*/ */
// http://www.whatwg.org/specs/web-apps/current-work/#the-a-element // http://www.whatwg.org/specs/web-apps/current-work/#the-a-element
[HTMLConstructor]
interface HTMLAnchorElement : HTMLElement { interface HTMLAnchorElement : HTMLElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString target; attribute DOMString target;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString download; attribute DOMString download;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString ping; attribute DOMString ping;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString rel; attribute DOMString rel;
[SetterThrows, Pref="network.http.enablePerElementReferrer"] [CEReactions, SetterThrows, Pref="network.http.enablePerElementReferrer"]
attribute DOMString referrerPolicy; attribute DOMString referrerPolicy;
[PutForwards=value] [PutForwards=value]
readonly attribute DOMTokenList relList; readonly attribute DOMTokenList relList;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString hreflang; attribute DOMString hreflang;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString type; attribute DOMString type;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString text; attribute DOMString text;
}; };
@ -38,14 +39,14 @@ HTMLAnchorElement implements HTMLHyperlinkElementUtils;
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
partial interface HTMLAnchorElement { partial interface HTMLAnchorElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString coords; attribute DOMString coords;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString charset; attribute DOMString charset;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString name; attribute DOMString name;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString rev; attribute DOMString rev;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString shape; attribute DOMString shape;
}; };

View File

@ -13,20 +13,21 @@
*/ */
// http://www.whatwg.org/specs/web-apps/current-work/#the-area-element // http://www.whatwg.org/specs/web-apps/current-work/#the-area-element
[HTMLConstructor]
interface HTMLAreaElement : HTMLElement { interface HTMLAreaElement : HTMLElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString alt; attribute DOMString alt;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString coords; attribute DOMString coords;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString shape; attribute DOMString shape;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString target; attribute DOMString target;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString download; attribute DOMString download;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString ping; attribute DOMString ping;
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString rel; attribute DOMString rel;
[SetterThrows, Pref="network.http.enablePerElementReferrer"] [SetterThrows, Pref="network.http.enablePerElementReferrer"]
attribute DOMString referrerPolicy; attribute DOMString referrerPolicy;
@ -38,6 +39,6 @@ HTMLAreaElement implements HTMLHyperlinkElementUtils;
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
partial interface HTMLAreaElement { partial interface HTMLAreaElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute boolean noHref; attribute boolean noHref;
}; };

View File

@ -11,6 +11,6 @@
* and create derivative works of this document. * and create derivative works of this document.
*/ */
[NamedConstructor=Audio(optional DOMString src)] [HTMLConstructor, NamedConstructor=Audio(optional DOMString src)]
interface HTMLAudioElement : HTMLMediaElement {}; interface HTMLAudioElement : HTMLMediaElement {};

View File

@ -13,11 +13,12 @@
*/ */
// http://www.whatwg.org/specs/web-apps/current-work/#the-br-element // http://www.whatwg.org/specs/web-apps/current-work/#the-br-element
[HTMLConstructor]
interface HTMLBRElement : HTMLElement {}; interface HTMLBRElement : HTMLElement {};
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
partial interface HTMLBRElement { partial interface HTMLBRElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString clear; attribute DOMString clear;
}; };

View File

@ -12,10 +12,11 @@
*/ */
// http://www.whatwg.org/specs/web-apps/current-work/#the-base-element // http://www.whatwg.org/specs/web-apps/current-work/#the-base-element
[HTMLConstructor]
interface HTMLBaseElement : HTMLElement { interface HTMLBaseElement : HTMLElement {
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString href; attribute DOMString href;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString target; attribute DOMString target;
}; };

View File

@ -11,16 +11,23 @@
* and create derivative works of this document. * and create derivative works of this document.
*/ */
[HTMLConstructor]
interface HTMLBodyElement : HTMLElement { interface HTMLBodyElement : HTMLElement {
}; };
partial interface HTMLBodyElement { partial interface HTMLBodyElement {
[TreatNullAs=EmptyString, SetterThrows] attribute DOMString text; [CEReactions, TreatNullAs=EmptyString, SetterThrows]
[TreatNullAs=EmptyString, SetterThrows] attribute DOMString link; attribute DOMString text;
[TreatNullAs=EmptyString, SetterThrows] attribute DOMString vLink; [CEReactions, TreatNullAs=EmptyString, SetterThrows]
[TreatNullAs=EmptyString, SetterThrows] attribute DOMString aLink; attribute DOMString link;
[TreatNullAs=EmptyString, SetterThrows] attribute DOMString bgColor; [CEReactions, TreatNullAs=EmptyString, SetterThrows]
[SetterThrows] attribute DOMString background; attribute DOMString vLink;
[CEReactions, TreatNullAs=EmptyString, SetterThrows]
attribute DOMString aLink;
[CEReactions, TreatNullAs=EmptyString, SetterThrows]
attribute DOMString bgColor;
[CEReactions, SetterThrows]
attribute DOMString background;
}; };
HTMLBodyElement implements WindowEventHandlers; HTMLBodyElement implements WindowEventHandlers;

View File

@ -11,28 +11,29 @@
*/ */
// http://www.whatwg.org/specs/web-apps/current-work/#the-button-element // http://www.whatwg.org/specs/web-apps/current-work/#the-button-element
[HTMLConstructor]
interface HTMLButtonElement : HTMLElement { interface HTMLButtonElement : HTMLElement {
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute boolean autofocus; attribute boolean autofocus;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute boolean disabled; attribute boolean disabled;
[Pure] [Pure]
readonly attribute HTMLFormElement? form; readonly attribute HTMLFormElement? form;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString formAction; attribute DOMString formAction;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString formEnctype; attribute DOMString formEnctype;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString formMethod; attribute DOMString formMethod;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute boolean formNoValidate; attribute boolean formNoValidate;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString formTarget; attribute DOMString formTarget;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString name; attribute DOMString name;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString type; attribute DOMString type;
[SetterThrows, Pure] [CEReactions, SetterThrows, Pure]
attribute DOMString value; attribute DOMString value;
// Not yet implemented: // Not yet implemented:
// attribute HTMLMenuElement? menu; // attribute HTMLMenuElement? menu;

View File

@ -13,10 +13,11 @@
interface nsISupports; interface nsISupports;
interface Variant; interface Variant;
[HTMLConstructor]
interface HTMLCanvasElement : HTMLElement { interface HTMLCanvasElement : HTMLElement {
[Pure, SetterThrows] [CEReactions, Pure, SetterThrows]
attribute unsigned long width; attribute unsigned long width;
[Pure, SetterThrows] [CEReactions, Pure, SetterThrows]
attribute unsigned long height; attribute unsigned long height;
[Throws] [Throws]

View File

@ -13,11 +13,12 @@
*/ */
// http://www.whatwg.org/specs/web-apps/current-work/#the-dl-element // http://www.whatwg.org/specs/web-apps/current-work/#the-dl-element
[HTMLConstructor]
interface HTMLDListElement : HTMLElement { interface HTMLDListElement : HTMLElement {
}; };
// http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
partial interface HTMLDListElement { partial interface HTMLDListElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute boolean compact; attribute boolean compact;
}; };

View File

@ -7,7 +7,8 @@
* http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-data-element * http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-data-element
*/ */
[HTMLConstructor]
interface HTMLDataElement : HTMLElement { interface HTMLDataElement : HTMLElement {
[SetterThrows] [CEReactions, SetterThrows]
attribute DOMString value; attribute DOMString value;
}; };

Some files were not shown because too many files have changed in this diff Show More