mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 02:09:28 +00:00
Bug 1275835 - Part 1: Move custom element codes from nsDocument to CustomElementsRegistry; r=wchen
MozReview-Commit-ID: 9gTSFrYW7o3 --HG-- extra : rebase_source : f123f21aadaa18641ddd7fa7fa67eb27a4152f83
This commit is contained in:
parent
5a59692088
commit
67946850ab
@ -5,17 +5,149 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/CustomElementsRegistry.h"
|
||||
|
||||
#include "mozilla/dom/CustomElementsRegistryBinding.h"
|
||||
#include "mozilla/dom/WebComponentsBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
void
|
||||
CustomElementCallback::Call()
|
||||
{
|
||||
ErrorResult rv;
|
||||
switch (mType) {
|
||||
case nsIDocument::eCreated:
|
||||
{
|
||||
// For the duration of this callback invocation, the element is being created
|
||||
// flag must be set to true.
|
||||
mOwnerData->mElementIsBeingCreated = true;
|
||||
|
||||
// The callback hasn't actually been invoked yet, but we need to flip
|
||||
// this now in order to enqueue the attached callback. This is a spec
|
||||
// bug (w3c bug 27437).
|
||||
mOwnerData->mCreatedCallbackInvoked = true;
|
||||
|
||||
// If ELEMENT is in a document and this document has a browsing context,
|
||||
// enqueue attached callback for ELEMENT.
|
||||
nsIDocument* document = mThisObject->GetComposedDoc();
|
||||
if (document && document->GetDocShell()) {
|
||||
nsContentUtils::EnqueueLifecycleCallback(
|
||||
document, nsIDocument::eAttached, mThisObject);
|
||||
}
|
||||
|
||||
static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
|
||||
mOwnerData->mElementIsBeingCreated = false;
|
||||
break;
|
||||
}
|
||||
case nsIDocument::eAttached:
|
||||
static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
|
||||
break;
|
||||
case nsIDocument::eDetached:
|
||||
static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
|
||||
break;
|
||||
case nsIDocument::eAttributeChanged:
|
||||
static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
|
||||
mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
|
||||
{
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
|
||||
aCb.NoteXPCOMChild(mThisObject);
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
|
||||
aCb.NoteXPCOMChild(mCallback);
|
||||
}
|
||||
|
||||
CustomElementCallback::CustomElementCallback(Element* aThisObject,
|
||||
nsIDocument::ElementCallbackType aCallbackType,
|
||||
mozilla::dom::CallbackFunction* aCallback,
|
||||
CustomElementData* aOwnerData)
|
||||
: mThisObject(aThisObject),
|
||||
mCallback(aCallback),
|
||||
mType(aCallbackType),
|
||||
mOwnerData(aOwnerData)
|
||||
{
|
||||
}
|
||||
|
||||
CustomElementData::CustomElementData(nsIAtom* aType)
|
||||
: mType(aType),
|
||||
mCurrentCallback(-1),
|
||||
mElementIsBeingCreated(false),
|
||||
mCreatedCallbackInvoked(true),
|
||||
mAssociatedMicroTask(-1)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementData::RunCallbackQueue()
|
||||
{
|
||||
// Note: It's possible to re-enter this method.
|
||||
while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
|
||||
mCallbackQueue[mCurrentCallback]->Call();
|
||||
}
|
||||
|
||||
mCallbackQueue.Clear();
|
||||
mCurrentCallback = -1;
|
||||
}
|
||||
|
||||
// Only needed for refcounted objects.
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CustomElementsRegistry, mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementsRegistry)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementsRegistry)
|
||||
tmp->mCustomDefinitions.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementsRegistry)
|
||||
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsAutoPtr<LifecycleCallbacks>& callbacks = iter.UserData()->mCallbacks;
|
||||
|
||||
if (callbacks->mAttributeChangedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mCreatedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mCreatedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mAttachedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mAttachedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mDetachedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mDetachedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementsRegistry)
|
||||
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
|
||||
aCallbacks.Trace(&iter.UserData()->mPrototype,
|
||||
"mCustomDefinitions prototype",
|
||||
aClosure);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementsRegistry)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementsRegistry)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementsRegistry)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
@ -43,18 +175,291 @@ CustomElementsRegistry::Create(nsPIDOMWindowInner* aWindow)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!Preferences::GetBool("dom.webcomponents.enabled") &&
|
||||
!Preferences::GetBool("dom.webcomponents.customelement.enabled")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementsRegistry> customElementsRegistry =
|
||||
new CustomElementsRegistry(aWindow);
|
||||
return customElementsRegistry.forget();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CustomElementsRegistry::ProcessTopElementQueue()
|
||||
{
|
||||
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
||||
|
||||
nsTArray<RefPtr<CustomElementData>>& stack = *sProcessingStack;
|
||||
uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
|
||||
|
||||
for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
|
||||
// Callback queue may have already been processed in an earlier
|
||||
// element queue or in an element queue that was popped
|
||||
// off more recently.
|
||||
if (stack[i]->mAssociatedMicroTask != -1) {
|
||||
stack[i]->RunCallbackQueue();
|
||||
stack[i]->mAssociatedMicroTask = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If this was actually the base element queue, don't bother trying to pop
|
||||
// the first "queue" marker (sentinel).
|
||||
if (firstQueue != 0) {
|
||||
stack.SetLength(firstQueue);
|
||||
} else {
|
||||
// Don't pop sentinel for base element queue.
|
||||
stack.SetLength(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CustomElementsRegistry::XPCOMShutdown()
|
||||
{
|
||||
sProcessingStack.reset();
|
||||
}
|
||||
|
||||
/* static */ Maybe<nsTArray<RefPtr<CustomElementData>>>
|
||||
CustomElementsRegistry::sProcessingStack;
|
||||
|
||||
CustomElementsRegistry::CustomElementsRegistry(nsPIDOMWindowInner* aWindow)
|
||||
: mWindow(aWindow)
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
|
||||
if (!sProcessingStack) {
|
||||
sProcessingStack.emplace();
|
||||
// Add the base queue sentinel to the processing stack.
|
||||
sProcessingStack->AppendElement((CustomElementData*) nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
CustomElementsRegistry::~CustomElementsRegistry()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
CustomElementDefinition*
|
||||
CustomElementsRegistry::LookupCustomElementDefinition(const nsAString& aLocalName,
|
||||
const nsAString* aIs) const
|
||||
{
|
||||
nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
|
||||
nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
|
||||
|
||||
CustomElementHashKey key(kNameSpaceID_XHTML, typeAtom);
|
||||
CustomElementDefinition* data = mCustomDefinitions.Get(&key);
|
||||
if (data && data->mLocalName == localNameAtom) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementsRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
|
||||
{
|
||||
mozilla::dom::NodeInfo* info = aElement->NodeInfo();
|
||||
|
||||
// Candidate may be a custom element through extension,
|
||||
// in which case the custom element type name will not
|
||||
// match the element tag name. e.g. <button is="x-button">.
|
||||
nsCOMPtr<nsIAtom> typeName = aTypeName;
|
||||
if (!typeName) {
|
||||
typeName = info->NameAtom();
|
||||
}
|
||||
|
||||
CustomElementHashKey key(info->NamespaceID(), typeName);
|
||||
if (mCustomDefinitions.Get(&key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsWeakPtr>* unresolved = mCandidatesMap.Get(&key);
|
||||
if (!unresolved) {
|
||||
unresolved = new nsTArray<nsWeakPtr>();
|
||||
// Ownership of unresolved is taken by customElements.
|
||||
mCandidatesMap.Put(&key, unresolved);
|
||||
}
|
||||
|
||||
nsWeakPtr* elem = unresolved->AppendElement();
|
||||
*elem = do_GetWeakReference(aElement);
|
||||
aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementsRegistry::SetupCustomElement(Element* aElement,
|
||||
const nsAString* aTypeExtension)
|
||||
{
|
||||
nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
|
||||
nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
|
||||
NS_Atomize(*aTypeExtension) : tagAtom;
|
||||
|
||||
if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
|
||||
// Custom element setup in the parser happens after the "is"
|
||||
// attribute is added.
|
||||
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
|
||||
}
|
||||
|
||||
CustomElementDefinition* data = LookupCustomElementDefinition(
|
||||
aElement->NodeInfo()->LocalName(), aTypeExtension);
|
||||
|
||||
if (!data) {
|
||||
// The type extension doesn't exist in the registry,
|
||||
// thus we don't need to enqueue callback or adjust
|
||||
// the "is" attribute, but it is possibly an upgrade candidate.
|
||||
RegisterUnresolvedElement(aElement, typeAtom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->mLocalName != tagAtom) {
|
||||
// The element doesn't match the local name for the
|
||||
// definition, thus the element isn't a custom element
|
||||
// and we don't need to do anything more.
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueuing the created callback will set the CustomElementData on the
|
||||
// element, causing prototype swizzling to occur in Element::WrapObject.
|
||||
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementsRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
LifecycleCallbackArgs* aArgs,
|
||||
CustomElementDefinition* aDefinition)
|
||||
{
|
||||
CustomElementData* elementData = aCustomElement->GetCustomElementData();
|
||||
|
||||
// Let DEFINITION be ELEMENT's definition
|
||||
CustomElementDefinition* definition = aDefinition;
|
||||
if (!definition) {
|
||||
mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
|
||||
|
||||
// Make sure we get the correct definition in case the element
|
||||
// is a extended custom element e.g. <button is="x-button">.
|
||||
nsCOMPtr<nsIAtom> typeAtom = elementData ?
|
||||
elementData->mType.get() : info->NameAtom();
|
||||
|
||||
CustomElementHashKey key(info->NamespaceID(), typeAtom);
|
||||
definition = mCustomDefinitions.Get(&key);
|
||||
if (!definition || definition->mLocalName != info->NameAtom()) {
|
||||
// Trying to enqueue a callback for an element that is not
|
||||
// a custom element. We are done, nothing to do.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!elementData) {
|
||||
// Create the custom element data the first time
|
||||
// that we try to enqueue a callback.
|
||||
elementData = new CustomElementData(definition->mType);
|
||||
// aCustomElement takes ownership of elementData
|
||||
aCustomElement->SetCustomElementData(elementData);
|
||||
MOZ_ASSERT(aType == nsIDocument::eCreated,
|
||||
"First callback should be the created callback");
|
||||
}
|
||||
|
||||
// Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
|
||||
CallbackFunction* func = nullptr;
|
||||
switch (aType) {
|
||||
case nsIDocument::eCreated:
|
||||
if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mCreatedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eAttached:
|
||||
if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mAttachedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eDetached:
|
||||
if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mDetachedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eAttributeChanged:
|
||||
if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mAttributeChangedCallback.Value();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is no such callback, stop.
|
||||
if (!func) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aType == nsIDocument::eCreated) {
|
||||
elementData->mCreatedCallbackInvoked = false;
|
||||
} else if (!elementData->mCreatedCallbackInvoked) {
|
||||
// Callbacks other than created callback must not be enqueued
|
||||
// until after the created callback has been invoked.
|
||||
return;
|
||||
}
|
||||
|
||||
// Add CALLBACK to ELEMENT's callback queue.
|
||||
CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
|
||||
aType,
|
||||
func,
|
||||
elementData);
|
||||
// Ownership of callback is taken by mCallbackQueue.
|
||||
elementData->mCallbackQueue.AppendElement(callback);
|
||||
if (aArgs) {
|
||||
callback->SetArgs(*aArgs);
|
||||
}
|
||||
|
||||
if (!elementData->mElementIsBeingCreated) {
|
||||
CustomElementData* lastData =
|
||||
sProcessingStack->SafeLastElement(nullptr);
|
||||
|
||||
// A new element queue needs to be pushed if the queue at the
|
||||
// top of the stack is associated with another microtask level.
|
||||
bool shouldPushElementQueue =
|
||||
(!lastData || lastData->mAssociatedMicroTask <
|
||||
static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
|
||||
|
||||
// Push a new element queue onto the processing stack when appropriate
|
||||
// (when we enter a new microtask).
|
||||
if (shouldPushElementQueue) {
|
||||
// Push a sentinel value on the processing stack to mark the
|
||||
// boundary between the element queues.
|
||||
sProcessingStack->AppendElement((CustomElementData*) nullptr);
|
||||
}
|
||||
|
||||
sProcessingStack->AppendElement(elementData);
|
||||
elementData->mAssociatedMicroTask =
|
||||
static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
|
||||
|
||||
// Add a script runner to pop and process the element queue at
|
||||
// the top of the processing stack.
|
||||
if (shouldPushElementQueue) {
|
||||
// Lifecycle callbacks enqueued by user agent implementation
|
||||
// should be invoked prior to returning control back to script.
|
||||
// Create a script runner to process the top of the processing
|
||||
// stack as soon as it is safe to run script.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction(&CustomElementsRegistry::ProcessTopElementQueue);
|
||||
nsContentUtils::AddScriptRunner(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementsRegistry::GetCustomPrototype(nsIAtom* aAtom,
|
||||
JS::MutableHandle<JSObject*> aPrototype)
|
||||
{
|
||||
mozilla::dom::CustomElementHashKey key(kNameSpaceID_XHTML, aAtom);
|
||||
mozilla::dom::CustomElementDefinition* definition = mCustomDefinitions.Get(&key);
|
||||
if (definition) {
|
||||
aPrototype.set(definition->mPrototype);
|
||||
} else {
|
||||
aPrototype.set(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -68,10 +473,11 @@ nsISupports* CustomElementsRegistry::GetParentObject() const
|
||||
return mWindow;
|
||||
}
|
||||
|
||||
void CustomElementsRegistry::Define(const nsAString& aName,
|
||||
Function& aFunctionConstructor,
|
||||
const ElementDefinitionOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
void
|
||||
CustomElementsRegistry::Define(const nsAString& aName,
|
||||
Function& aFunctionConstructor,
|
||||
const ElementDefinitionOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// TODO: This function will be implemented in bug 1275835
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
@ -15,14 +15,87 @@
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
|
||||
class nsDocument;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct CustomElementData;
|
||||
struct ElementDefinitionOptions;
|
||||
struct LifecycleCallbacks;
|
||||
class CallbackFunction;
|
||||
class Function;
|
||||
class Promise;
|
||||
|
||||
struct LifecycleCallbackArgs
|
||||
{
|
||||
nsString name;
|
||||
nsString oldValue;
|
||||
nsString newValue;
|
||||
};
|
||||
|
||||
class CustomElementCallback
|
||||
{
|
||||
public:
|
||||
CustomElementCallback(Element* aThisObject,
|
||||
nsIDocument::ElementCallbackType aCallbackType,
|
||||
CallbackFunction* aCallback,
|
||||
CustomElementData* aOwnerData);
|
||||
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
|
||||
void Call();
|
||||
void SetArgs(LifecycleCallbackArgs& aArgs)
|
||||
{
|
||||
MOZ_ASSERT(mType == nsIDocument::eAttributeChanged,
|
||||
"Arguments are only used by attribute changed callback.");
|
||||
mArgs = aArgs;
|
||||
}
|
||||
|
||||
private:
|
||||
// The this value to use for invocation of the callback.
|
||||
RefPtr<Element> mThisObject;
|
||||
RefPtr<CallbackFunction> mCallback;
|
||||
// The type of callback (eCreated, eAttached, etc.)
|
||||
nsIDocument::ElementCallbackType mType;
|
||||
// Arguments to be passed to the callback,
|
||||
// used by the attribute changed callback.
|
||||
LifecycleCallbackArgs mArgs;
|
||||
// CustomElementData that contains this callback in the
|
||||
// callback queue.
|
||||
CustomElementData* mOwnerData;
|
||||
};
|
||||
|
||||
// Each custom element has an associated callback queue and an element is
|
||||
// being created flag.
|
||||
struct CustomElementData
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(CustomElementData)
|
||||
|
||||
explicit CustomElementData(nsIAtom* aType);
|
||||
// Objects in this array are transient and empty after each microtask
|
||||
// checkpoint.
|
||||
nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue;
|
||||
// Custom element type, for <button is="x-button"> or <x-button>
|
||||
// this would be x-button.
|
||||
nsCOMPtr<nsIAtom> mType;
|
||||
// The callback that is next to be processed upon calling RunCallbackQueue.
|
||||
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.
|
||||
void RunCallbackQueue();
|
||||
|
||||
private:
|
||||
virtual ~CustomElementData() {}
|
||||
};
|
||||
|
||||
class CustomElementHashKey : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
@ -101,6 +174,9 @@ struct CustomElementDefinition
|
||||
class CustomElementsRegistry final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
// Allow nsDocument to access mCustomDefinitions and mCandidatesMap.
|
||||
friend class ::nsDocument;
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementsRegistry)
|
||||
@ -108,13 +184,72 @@ public:
|
||||
public:
|
||||
static bool IsCustomElementsEnabled(JSContext* aCx, JSObject* aObject);
|
||||
static already_AddRefed<CustomElementsRegistry> Create(nsPIDOMWindowInner* aWindow);
|
||||
already_AddRefed<nsIDocument> GetOwnerDocument() const;
|
||||
static void ProcessTopElementQueue();
|
||||
|
||||
static void XPCOMShutdown();
|
||||
|
||||
/**
|
||||
* Looking up a custom element definition.
|
||||
* https://html.spec.whatwg.org/#look-up-a-custom-element-definition
|
||||
*/
|
||||
CustomElementDefinition* LookupCustomElementDefinition(
|
||||
const nsAString& aLocalName, const nsAString* aIs = nullptr) const;
|
||||
|
||||
/**
|
||||
* Enqueue created callback or register upgrade candidate for
|
||||
* 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,
|
||||
Element* aCustomElement,
|
||||
LifecycleCallbackArgs* aArgs,
|
||||
CustomElementDefinition* aDefinition);
|
||||
|
||||
void GetCustomPrototype(nsIAtom* aAtom,
|
||||
JS::MutableHandle<JSObject*> aPrototype);
|
||||
|
||||
private:
|
||||
explicit CustomElementsRegistry(nsPIDOMWindowInner* aWindow);
|
||||
~CustomElementsRegistry();
|
||||
|
||||
/**
|
||||
* Registers an unresolved custom element that is a candidate for
|
||||
* upgrade when the definition is registered via registerElement.
|
||||
* |aTypeName| is the name of the custom element type, if it is not
|
||||
* provided, then element name is used. |aTypeName| should be provided
|
||||
* when registering a custom element that extends an existing
|
||||
* element. e.g. <button is="x-button">.
|
||||
*/
|
||||
void RegisterUnresolvedElement(Element* aElement,
|
||||
nsIAtom* aTypeName = nullptr);
|
||||
|
||||
typedef nsClassHashtable<CustomElementHashKey, CustomElementDefinition>
|
||||
DefinitionMap;
|
||||
typedef nsClassHashtable<CustomElementHashKey, nsTArray<nsWeakPtr>>
|
||||
CandidateMap;
|
||||
|
||||
// Hashtable for custom element definitions in web components.
|
||||
// Custom prototypes are stored in the compartment where
|
||||
// registerElement was called.
|
||||
DefinitionMap mCustomDefinitions;
|
||||
|
||||
// The "upgrade candidates map" from the web components spec. Maps from a
|
||||
// namespace id and local name to a list of elements to upgrade if that
|
||||
// element is registered as a custom element.
|
||||
CandidateMap mCandidatesMap;
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
nsISupports* GetParentObject() const;
|
||||
|
||||
|
@ -133,7 +133,6 @@ DOMImplementation::CreateDocument(const nsAString& aNamespaceURI,
|
||||
|
||||
if (aNamespaceURI.EqualsLiteral("http://www.w3.org/1999/xhtml")) {
|
||||
doc->SetContentType(NS_LITERAL_STRING("application/xhtml+xml"));
|
||||
doc->UseRegistryFromDocument(mOwner);
|
||||
} else if (aNamespaceURI.EqualsLiteral("http://www.w3.org/2000/svg")) {
|
||||
doc->SetContentType(NS_LITERAL_STRING("image/svg+xml"));
|
||||
} else {
|
||||
@ -235,10 +234,6 @@ DOMImplementation::CreateHTMLDocument(const nsAString& aTitle,
|
||||
rv = root->AppendChildTo(body, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// When the createHTMLDocument method is invoked,
|
||||
// use the registry of the associated document to the new instance.
|
||||
doc->UseRegistryFromDocument(mOwner);
|
||||
|
||||
doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
|
||||
|
||||
doc.forget(aDocument);
|
||||
|
@ -470,8 +470,8 @@ Element::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
CustomElementData* data = GetCustomElementData();
|
||||
if (data) {
|
||||
// If this is a registered custom element then fix the prototype.
|
||||
nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
|
||||
document->GetCustomPrototype(NodeInfo()->NamespaceID(), data->mType, &customProto);
|
||||
nsContentUtils::GetCustomPrototype(OwnerDoc(), NodeInfo()->NamespaceID(),
|
||||
data->mType, &customProto);
|
||||
if (customProto &&
|
||||
NodePrincipal()->SubsumesConsideringDomain(nsContentUtils::ObjectPrincipal(customProto))) {
|
||||
// Just go ahead and create with the right proto up front. Set
|
||||
@ -1608,7 +1608,8 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
// document and this document has a browsing context.
|
||||
if (GetCustomElementData() && composedDoc->GetDocShell()) {
|
||||
// Enqueue an attached callback for the custom element.
|
||||
composedDoc->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
|
||||
nsContentUtils::EnqueueLifecycleCallback(
|
||||
composedDoc, nsIDocument::eAttached, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1889,7 +1890,8 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
// the document and this document has a browsing context.
|
||||
if (GetCustomElementData() && document->GetDocShell()) {
|
||||
// Enqueue a detached callback for the custom element.
|
||||
document->EnqueueLifecycleCallback(nsIDocument::eDetached, this);
|
||||
nsContentUtils::EnqueueLifecycleCallback(
|
||||
document, nsIDocument::eDetached, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2495,7 +2497,8 @@ Element::SetAttrAndNotify(int32_t aNamespaceID,
|
||||
nsDependentAtomString(newValueAtom)
|
||||
};
|
||||
|
||||
ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
|
||||
nsContentUtils::EnqueueLifecycleCallback(
|
||||
ownerDoc, nsIDocument::eAttributeChanged, this, &args);
|
||||
}
|
||||
|
||||
if (aCallAfterSetAttr) {
|
||||
@ -2749,7 +2752,8 @@ Element::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aName,
|
||||
NullString()
|
||||
};
|
||||
|
||||
ownerDoc->EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, this, &args);
|
||||
nsContentUtils::EnqueueLifecycleCallback(
|
||||
ownerDoc, nsIDocument::eAttributeChanged, this, &args);
|
||||
}
|
||||
|
||||
if (aNotify) {
|
||||
|
@ -136,6 +136,7 @@ class EventStateManager;
|
||||
namespace dom {
|
||||
|
||||
class Animation;
|
||||
class CustomElementsRegistry;
|
||||
class Link;
|
||||
class UndoManager;
|
||||
class DOMRect;
|
||||
@ -426,6 +427,9 @@ private:
|
||||
friend class ::nsFocusManager;
|
||||
friend class ::nsDocument;
|
||||
|
||||
// Allow CusomtElementRegistry to call AddStates.
|
||||
friend class CustomElementsRegistry;
|
||||
|
||||
// Also need to allow Link to call UpdateLinkState.
|
||||
friend class Link;
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/CustomElementsRegistry.h"
|
||||
#include "mozilla/dom/DocumentFragment.h"
|
||||
#include "mozilla/dom/DOMTypes.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
@ -9359,3 +9360,123 @@ nsContentUtils::HttpsStateIsModern(nsIDocument* aDocument)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ CustomElementDefinition*
|
||||
nsContentUtils::LookupCustomElementDefinition(nsIDocument* aDoc,
|
||||
const nsAString& aLocalName,
|
||||
uint32_t aNameSpaceID,
|
||||
const nsAString* aIs)
|
||||
{
|
||||
MOZ_ASSERT(aDoc);
|
||||
|
||||
// To support imported document.
|
||||
nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument();
|
||||
|
||||
if (aNameSpaceID != kNameSpaceID_XHTML ||
|
||||
!doc->GetDocShell()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementsRegistry> registry(window->CustomElements());
|
||||
if (!registry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return registry->LookupCustomElementDefinition(aLocalName, aIs);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::SetupCustomElement(Element* aElement,
|
||||
const nsAString* aTypeExtension)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = aElement->OwnerDoc();
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementsRegistry> registry(window->CustomElements());
|
||||
if (!registry) {
|
||||
return;
|
||||
}
|
||||
|
||||
return registry->SetupCustomElement(aElement, aTypeExtension);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::EnqueueLifecycleCallback(nsIDocument* aDoc,
|
||||
nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
LifecycleCallbackArgs* aArgs,
|
||||
CustomElementDefinition* aDefinition)
|
||||
{
|
||||
MOZ_ASSERT(aDoc);
|
||||
|
||||
// To support imported document.
|
||||
nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument();
|
||||
|
||||
if (!doc->GetDocShell()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementsRegistry> registry(window->CustomElements());
|
||||
if (!registry) {
|
||||
return;
|
||||
}
|
||||
|
||||
registry->EnqueueLifecycleCallback(aType, aCustomElement, aArgs, aDefinition);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsContentUtils::GetCustomPrototype(nsIDocument* aDoc,
|
||||
int32_t aNamespaceID,
|
||||
nsIAtom* aAtom,
|
||||
JS::MutableHandle<JSObject*> aPrototype)
|
||||
{
|
||||
MOZ_ASSERT(aDoc);
|
||||
|
||||
// To support imported document.
|
||||
nsCOMPtr<nsIDocument> doc = aDoc->MasterDocument();
|
||||
|
||||
if (aNamespaceID != kNameSpaceID_XHTML ||
|
||||
!doc->GetDocShell()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window(doc->GetInnerWindow());
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<CustomElementsRegistry> registry(window->CustomElements());
|
||||
if (!registry) {
|
||||
return;
|
||||
}
|
||||
|
||||
return registry->GetCustomPrototype(aAtom, aPrototype);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/NotNull.h"
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
@ -55,7 +56,6 @@ class nsIContent;
|
||||
class nsIContentPolicy;
|
||||
class nsIContentSecurityPolicy;
|
||||
class nsIDocShellTreeItem;
|
||||
class nsIDocument;
|
||||
class nsIDocumentLoaderFactory;
|
||||
class nsIDOMDocument;
|
||||
class nsIDOMDocumentFragment;
|
||||
@ -119,11 +119,13 @@ class ErrorResult;
|
||||
class EventListenerManager;
|
||||
|
||||
namespace dom {
|
||||
struct CustomElementDefinition;
|
||||
class DocumentFragment;
|
||||
class Element;
|
||||
class EventTarget;
|
||||
class IPCDataTransfer;
|
||||
class IPCDataTransferItem;
|
||||
struct LifecycleCallbackArgs;
|
||||
class NodeInfo;
|
||||
class nsIContentChild;
|
||||
class nsIContentParent;
|
||||
@ -2668,6 +2670,30 @@ public:
|
||||
*/
|
||||
static bool HttpsStateIsModern(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Looking up a custom element definition.
|
||||
* https://html.spec.whatwg.org/#look-up-a-custom-element-definition
|
||||
*/
|
||||
static mozilla::dom::CustomElementDefinition*
|
||||
LookupCustomElementDefinition(nsIDocument* aDoc,
|
||||
const nsAString& aLocalName,
|
||||
uint32_t aNameSpaceID,
|
||||
const nsAString* aIs = nullptr);
|
||||
|
||||
static void SetupCustomElement(Element* aElement,
|
||||
const nsAString* aTypeExtension = nullptr);
|
||||
|
||||
static void EnqueueLifecycleCallback(nsIDocument* aDoc,
|
||||
nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
|
||||
|
||||
static void GetCustomPrototype(nsIDocument* aDoc,
|
||||
int32_t aNamespaceID,
|
||||
nsIAtom* aAtom,
|
||||
JS::MutableHandle<JSObject*> prototype);
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
|
@ -218,6 +218,7 @@
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/UndoManager.h"
|
||||
#include "mozilla/dom/WebComponentsBinding.h"
|
||||
#include "mozilla/dom/CustomElementsRegistry.h"
|
||||
#include "nsFrame.h"
|
||||
#include "nsDOMCaretPosition.h"
|
||||
#include "nsIDOMHTMLTextAreaElement.h"
|
||||
@ -368,156 +369,6 @@ nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver
|
||||
}
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Registry)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry)
|
||||
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
|
||||
aCallbacks.Trace(&iter.UserData()->mPrototype,
|
||||
"mCustomDefinitions prototype",
|
||||
aClosure);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry)
|
||||
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsAutoPtr<LifecycleCallbacks>& callbacks = iter.UserData()->mCallbacks;
|
||||
|
||||
if (callbacks->mAttributeChangedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mCreatedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mCreatedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mAttachedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mAttachedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
|
||||
}
|
||||
|
||||
if (callbacks->mDetachedCallback.WasPassed()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mCustomDefinitions->mCallbacks->mDetachedCallback");
|
||||
cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry)
|
||||
tmp->mCustomDefinitions.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry)
|
||||
|
||||
Registry::Registry()
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
Registry::~Registry()
|
||||
{
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementCallback::Call()
|
||||
{
|
||||
ErrorResult rv;
|
||||
switch (mType) {
|
||||
case nsIDocument::eCreated:
|
||||
{
|
||||
// For the duration of this callback invocation, the element is being created
|
||||
// flag must be set to true.
|
||||
mOwnerData->mElementIsBeingCreated = true;
|
||||
|
||||
// The callback hasn't actually been invoked yet, but we need to flip
|
||||
// this now in order to enqueue the attached callback. This is a spec
|
||||
// bug (w3c bug 27437).
|
||||
mOwnerData->mCreatedCallbackInvoked = true;
|
||||
|
||||
// If ELEMENT is in a document and this document has a browsing context,
|
||||
// enqueue attached callback for ELEMENT.
|
||||
nsIDocument* document = mThisObject->GetComposedDoc();
|
||||
if (document && document->GetDocShell()) {
|
||||
document->EnqueueLifecycleCallback(nsIDocument::eAttached, mThisObject);
|
||||
}
|
||||
|
||||
static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
|
||||
mOwnerData->mElementIsBeingCreated = false;
|
||||
break;
|
||||
}
|
||||
case nsIDocument::eAttached:
|
||||
static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
|
||||
break;
|
||||
case nsIDocument::eDetached:
|
||||
static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
|
||||
break;
|
||||
case nsIDocument::eAttributeChanged:
|
||||
static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
|
||||
mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
|
||||
{
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
|
||||
aCb.NoteXPCOMChild(mThisObject);
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
|
||||
aCb.NoteXPCOMChild(mCallback);
|
||||
}
|
||||
|
||||
CustomElementCallback::CustomElementCallback(Element* aThisObject,
|
||||
nsIDocument::ElementCallbackType aCallbackType,
|
||||
mozilla::dom::CallbackFunction* aCallback,
|
||||
CustomElementData* aOwnerData)
|
||||
: mThisObject(aThisObject),
|
||||
mCallback(aCallback),
|
||||
mType(aCallbackType),
|
||||
mOwnerData(aOwnerData)
|
||||
{
|
||||
}
|
||||
|
||||
CustomElementData::CustomElementData(nsIAtom* aType)
|
||||
: mType(aType),
|
||||
mCurrentCallback(-1),
|
||||
mElementIsBeingCreated(false),
|
||||
mCreatedCallbackInvoked(true),
|
||||
mAssociatedMicroTask(-1)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CustomElementData::RunCallbackQueue()
|
||||
{
|
||||
// Note: It's possible to re-enter this method.
|
||||
while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
|
||||
mCallbackQueue[mCurrentCallback]->Call();
|
||||
}
|
||||
|
||||
mCallbackQueue.Clear();
|
||||
mCurrentCallback = -1;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
void
|
||||
nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
|
||||
Element* aNewElement,
|
||||
@ -1494,12 +1345,6 @@ nsDocument::nsDocument(const char* aContentType)
|
||||
// void state used to differentiate an empty source from an unselected source
|
||||
mPreloadPictureFoundSource.SetIsVoid(true);
|
||||
|
||||
if (!sProcessingStack) {
|
||||
sProcessingStack.emplace();
|
||||
// Add the base queue sentinel to the processing stack.
|
||||
sProcessingStack->AppendElement((CustomElementData*) nullptr);
|
||||
}
|
||||
|
||||
mEverInForeground = false;
|
||||
}
|
||||
|
||||
@ -1617,8 +1462,6 @@ nsDocument::~nsDocument()
|
||||
mInDestructor = true;
|
||||
mInUnlinkOrDeletion = true;
|
||||
|
||||
mRegistry = nullptr;
|
||||
|
||||
mozilla::DropJSObjects(this);
|
||||
|
||||
// Clear mObservers to keep it in sync with the mutationobserver list
|
||||
@ -1890,7 +1733,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
|
||||
|
||||
// Traverse all our nsCOMArrays.
|
||||
@ -1975,7 +1817,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
|
||||
@ -2225,13 +2066,6 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||
}
|
||||
mInUnlinkOrDeletion = oldVal;
|
||||
|
||||
if (!mMasterDocument) {
|
||||
// "When creating an import, use the registry of the master document."
|
||||
// Note: at this point the mMasterDocument is already set for imports
|
||||
// (and only for imports)
|
||||
mRegistry = nullptr;
|
||||
}
|
||||
|
||||
// Reset our stylesheets
|
||||
ResetStylesheetsToURI(aURI);
|
||||
|
||||
@ -4732,10 +4566,6 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
||||
}
|
||||
|
||||
MaybeRescheduleAnimationFrameNotifications();
|
||||
if (Preferences::GetBool("dom.webcomponents.enabled") ||
|
||||
Preferences::GetBool("dom.webcomponents.customelements.enabled")) {
|
||||
mRegistry = new Registry();
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the pointer to our window (or lack there of), to avoid
|
||||
@ -5561,26 +5391,29 @@ bool IsLowercaseASCII(const nsAString& aValue)
|
||||
return true;
|
||||
}
|
||||
|
||||
CustomElementDefinition*
|
||||
nsDocument::LookupCustomElementDefinition(const nsAString& aLocalName,
|
||||
uint32_t aNameSpaceID,
|
||||
const nsAString* aIs)
|
||||
already_AddRefed<mozilla::dom::CustomElementsRegistry>
|
||||
nsDocument::GetCustomElementsRegistry()
|
||||
{
|
||||
if (!mRegistry || aNameSpaceID != kNameSpaceID_XHTML) {
|
||||
nsAutoString contentType;
|
||||
GetContentType(contentType);
|
||||
if (!IsHTMLDocument() &&
|
||||
!contentType.EqualsLiteral("application/xhtml+xml")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
|
||||
nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
|
||||
|
||||
CustomElementDefinition* data;
|
||||
CustomElementHashKey key(aNameSpaceID, typeAtom);
|
||||
if (mRegistry->mCustomDefinitions.Get(&key, &data) &&
|
||||
data->mLocalName == localNameAtom) {
|
||||
return data;
|
||||
nsCOMPtr<nsPIDOMWindowInner> window(
|
||||
do_QueryInterface(mScriptGlobalObject ? mScriptGlobalObject
|
||||
: GetScopeObject()));
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
RefPtr<CustomElementsRegistry> registry = window->CustomElements();
|
||||
if (!registry) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return registry.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Element>
|
||||
@ -5612,48 +5445,6 @@ nsDocument::CreateElement(const nsAString& aTagName,
|
||||
return elem.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
const nsAString* aTypeExtension)
|
||||
{
|
||||
if (!mRegistry || aNamespaceID != kNameSpaceID_XHTML) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
|
||||
nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
|
||||
NS_Atomize(*aTypeExtension) : tagAtom;
|
||||
|
||||
if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
|
||||
// Custom element setup in the parser happens after the "is"
|
||||
// attribute is added.
|
||||
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
|
||||
}
|
||||
|
||||
CustomElementDefinition* data = LookupCustomElementDefinition(
|
||||
aElement->NodeInfo()->LocalName(), aNamespaceID, aTypeExtension);
|
||||
|
||||
if (!data) {
|
||||
// The type extension doesn't exist in the registry,
|
||||
// thus we don't need to enqueue callback or adjust
|
||||
// the "is" attribute, but it is possibly an upgrade candidate.
|
||||
RegisterUnresolvedElement(aElement, typeAtom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->mLocalName != tagAtom) {
|
||||
// The element doesn't match the local name for the
|
||||
// definition, thus the element isn't a custom element
|
||||
// and we don't need to do anything more.
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueuing the created callback will set the CustomElementData on the
|
||||
// element, causing prototype swizzling to occur in Element::WrapObject.
|
||||
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
|
||||
const nsAString& aQualifiedName,
|
||||
@ -5913,11 +5704,15 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<mozilla::dom::CustomElementsRegistry> registry = window->CustomElements();
|
||||
if (!registry) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
|
||||
CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom);
|
||||
CustomElementDefinition* definition;
|
||||
if (!document->mRegistry ||
|
||||
!document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
|
||||
CustomElementDefinition* definition = registry->mCustomDefinitions.Get(&key);
|
||||
if (!definition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5931,7 +5726,7 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
|
||||
// This element is a custom element by extension, thus we need to
|
||||
// do some special setup. For non-extended custom elements, this happens
|
||||
// when the element is created.
|
||||
document->SetupCustomElement(element, definition->mNamespaceID, &elemName);
|
||||
nsContentUtils::SetupCustomElement(element, &elemName);
|
||||
}
|
||||
|
||||
nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval());
|
||||
@ -5972,228 +5767,18 @@ nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
|
||||
{
|
||||
if (!mRegistry) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozilla::dom::NodeInfo* info = aElement->NodeInfo();
|
||||
|
||||
// Candidate may be a custom element through extension,
|
||||
// in which case the custom element type name will not
|
||||
// match the element tag name. e.g. <button is="x-button">.
|
||||
nsCOMPtr<nsIAtom> typeName = aTypeName;
|
||||
if (!typeName) {
|
||||
typeName = info->NameAtom();
|
||||
}
|
||||
|
||||
CustomElementHashKey key(info->NamespaceID(), typeName);
|
||||
if (mRegistry->mCustomDefinitions.Get(&key)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<nsWeakPtr>* unresolved;
|
||||
mRegistry->mCandidatesMap.Get(&key, &unresolved);
|
||||
if (!unresolved) {
|
||||
unresolved = new nsTArray<nsWeakPtr>();
|
||||
// Ownership of unresolved is taken by mCandidatesMap.
|
||||
mRegistry->mCandidatesMap.Put(&key, unresolved);
|
||||
}
|
||||
|
||||
nsWeakPtr* elem = unresolved->AppendElement();
|
||||
*elem = do_GetWeakReference(aElement);
|
||||
aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
LifecycleCallbackArgs* aArgs,
|
||||
CustomElementDefinition* aDefinition)
|
||||
{
|
||||
if (!mRegistry) {
|
||||
// The element might not belong to a document that
|
||||
// has a browsing context, and thus no registry.
|
||||
return;
|
||||
}
|
||||
|
||||
CustomElementData* elementData = aCustomElement->GetCustomElementData();
|
||||
|
||||
// Let DEFINITION be ELEMENT's definition
|
||||
CustomElementDefinition* definition = aDefinition;
|
||||
if (!definition) {
|
||||
mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
|
||||
|
||||
// Make sure we get the correct definition in case the element
|
||||
// is a extended custom element e.g. <button is="x-button">.
|
||||
nsCOMPtr<nsIAtom> typeAtom = elementData ?
|
||||
elementData->mType.get() : info->NameAtom();
|
||||
|
||||
CustomElementHashKey key(info->NamespaceID(), typeAtom);
|
||||
if (!mRegistry->mCustomDefinitions.Get(&key, &definition) ||
|
||||
definition->mLocalName != info->NameAtom()) {
|
||||
// Trying to enqueue a callback for an element that is not
|
||||
// a custom element. We are done, nothing to do.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!elementData) {
|
||||
// Create the custom element data the first time
|
||||
// that we try to enqueue a callback.
|
||||
elementData = new CustomElementData(definition->mType);
|
||||
// aCustomElement takes ownership of elementData
|
||||
aCustomElement->SetCustomElementData(elementData);
|
||||
MOZ_ASSERT(aType == nsIDocument::eCreated,
|
||||
"First callback should be the created callback");
|
||||
}
|
||||
|
||||
// Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
|
||||
CallbackFunction* func = nullptr;
|
||||
switch (aType) {
|
||||
case nsIDocument::eCreated:
|
||||
if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mCreatedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eAttached:
|
||||
if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mAttachedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eDetached:
|
||||
if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mDetachedCallback.Value();
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIDocument::eAttributeChanged:
|
||||
if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
|
||||
func = definition->mCallbacks->mAttributeChangedCallback.Value();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is no such callback, stop.
|
||||
if (!func) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aType == nsIDocument::eCreated) {
|
||||
elementData->mCreatedCallbackInvoked = false;
|
||||
} else if (!elementData->mCreatedCallbackInvoked) {
|
||||
// Callbacks other than created callback must not be enqueued
|
||||
// until after the created callback has been invoked.
|
||||
return;
|
||||
}
|
||||
|
||||
// Add CALLBACK to ELEMENT's callback queue.
|
||||
CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
|
||||
aType,
|
||||
func,
|
||||
elementData);
|
||||
// Ownership of callback is taken by mCallbackQueue.
|
||||
elementData->mCallbackQueue.AppendElement(callback);
|
||||
if (aArgs) {
|
||||
callback->SetArgs(*aArgs);
|
||||
}
|
||||
|
||||
if (!elementData->mElementIsBeingCreated) {
|
||||
CustomElementData* lastData =
|
||||
sProcessingStack->SafeLastElement(nullptr);
|
||||
|
||||
// A new element queue needs to be pushed if the queue at the
|
||||
// top of the stack is associated with another microtask level.
|
||||
bool shouldPushElementQueue =
|
||||
(!lastData || lastData->mAssociatedMicroTask <
|
||||
static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
|
||||
|
||||
// Push a new element queue onto the processing stack when appropriate
|
||||
// (when we enter a new microtask).
|
||||
if (shouldPushElementQueue) {
|
||||
// Push a sentinel value on the processing stack to mark the
|
||||
// boundary between the element queues.
|
||||
sProcessingStack->AppendElement((CustomElementData*) nullptr);
|
||||
}
|
||||
|
||||
sProcessingStack->AppendElement(elementData);
|
||||
elementData->mAssociatedMicroTask =
|
||||
static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
|
||||
|
||||
// Add a script runner to pop and process the element queue at
|
||||
// the top of the processing stack.
|
||||
if (shouldPushElementQueue) {
|
||||
// Lifecycle callbacks enqueued by user agent implementation
|
||||
// should be invoked prior to returning control back to script.
|
||||
// Create a script runner to process the top of the processing
|
||||
// stack as soon as it is safe to run script.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction(&nsDocument::ProcessTopElementQueue);
|
||||
nsContentUtils::AddScriptRunner(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
nsDocument::ProcessTopElementQueue()
|
||||
{
|
||||
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
||||
|
||||
nsTArray<RefPtr<CustomElementData>>& stack = *sProcessingStack;
|
||||
uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
|
||||
|
||||
for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
|
||||
// Callback queue may have already been processed in an earlier
|
||||
// element queue or in an element queue that was popped
|
||||
// off more recently.
|
||||
if (stack[i]->mAssociatedMicroTask != -1) {
|
||||
stack[i]->RunCallbackQueue();
|
||||
stack[i]->mAssociatedMicroTask = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// If this was actually the base element queue, don't bother trying to pop
|
||||
// the first "queue" marker (sentinel).
|
||||
if (firstQueue != 0) {
|
||||
stack.SetLength(firstQueue);
|
||||
} else {
|
||||
// Don't pop sentinel for base element queue.
|
||||
stack.SetLength(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::RegisterEnabled()
|
||||
{
|
||||
static bool sPrefValue =
|
||||
Preferences::GetBool("dom.webcomponents.enabled", false);
|
||||
return sPrefValue;
|
||||
}
|
||||
|
||||
// static
|
||||
Maybe<nsTArray<RefPtr<mozilla::dom::CustomElementData>>>
|
||||
nsDocument::sProcessingStack;
|
||||
|
||||
void
|
||||
nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
const ElementRegistrationOptions& aOptions,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& rv)
|
||||
{
|
||||
if (!mRegistry) {
|
||||
RefPtr<CustomElementsRegistry> registry(GetCustomElementsRegistry());
|
||||
if (!registry) {
|
||||
rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
|
||||
|
||||
// Unconditionally convert TYPE to lowercase.
|
||||
nsAutoString lcType;
|
||||
nsContentUtils::ASCIIToLower(aType, lcType);
|
||||
@ -6217,7 +5802,7 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
// DuplicateDefinition and stop.
|
||||
// Note that we need to find existing custom elements from either namespace.
|
||||
CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom);
|
||||
if (definitions.Get(&duplicateFinder)) {
|
||||
if (registry->mCustomDefinitions.Get(&duplicateFinder)) {
|
||||
rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
@ -6359,11 +5944,11 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
callbacks,
|
||||
namespaceID,
|
||||
0 /* TODO dependent on HTML imports. Bug 877072 */);
|
||||
definitions.Put(&key, definition);
|
||||
registry->mCustomDefinitions.Put(&key, definition);
|
||||
|
||||
// Do element upgrade.
|
||||
nsAutoPtr<nsTArray<nsWeakPtr>> candidates;
|
||||
mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
|
||||
registry->mCandidatesMap.RemoveAndForget(&key, candidates);
|
||||
if (candidates) {
|
||||
for (size_t i = 0; i < candidates->Length(); ++i) {
|
||||
nsCOMPtr<Element> elem = do_QueryReferent(candidates->ElementAt(i));
|
||||
@ -6397,7 +5982,10 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
}
|
||||
}
|
||||
|
||||
EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
|
||||
if (GetDocShell()) {
|
||||
nsContentUtils::EnqueueLifecycleCallback(
|
||||
this, nsIDocument::eCreated, elem, nullptr, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6435,14 +6023,6 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
aRetval.set(wrappedConstructor);
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::UseRegistryFromDocument(nsIDocument* aDocument)
|
||||
{
|
||||
nsDocument* doc = static_cast<nsDocument*>(aDocument);
|
||||
MOZ_ASSERT(!mRegistry, "There should be no existing registry.");
|
||||
mRegistry = doc->mRegistry;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocument::GetElementsByTagName(const nsAString& aTagname,
|
||||
nsIDOMNodeList** aReturn)
|
||||
@ -8987,8 +8567,6 @@ nsDocument::Destroy()
|
||||
// leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
|
||||
// tearing down all those frame trees right now is the right thing to do.
|
||||
mExternalResourceMap.Shutdown();
|
||||
|
||||
mRegistry = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
@ -12577,12 +12155,6 @@ nsDocument::OnAppThemeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::XPCOMShutdown()
|
||||
{
|
||||
sProcessingStack.reset();
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::UpdateVisibilityState()
|
||||
{
|
||||
@ -13467,7 +13039,8 @@ nsDocument::CheckCustomElementName(const ElementCreationOptions& aOptions,
|
||||
nsString* is = const_cast<nsString*>(&(aOptions.mIs.Value()));
|
||||
|
||||
// Throw NotFoundError if 'is' is not-null and definition is null
|
||||
if (!LookupCustomElementDefinition(aLocalName, aNamespaceID, is)) {
|
||||
if (!nsContentUtils::LookupCustomElementDefinition(this, aLocalName,
|
||||
aNamespaceID, is)) {
|
||||
rv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
|
||||
}
|
||||
|
||||
|
@ -263,107 +263,6 @@ private:
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
struct LifecycleCallbackArgs
|
||||
{
|
||||
nsString name;
|
||||
nsString oldValue;
|
||||
nsString newValue;
|
||||
};
|
||||
|
||||
struct CustomElementData;
|
||||
|
||||
class CustomElementCallback
|
||||
{
|
||||
public:
|
||||
CustomElementCallback(Element* aThisObject,
|
||||
nsIDocument::ElementCallbackType aCallbackType,
|
||||
mozilla::dom::CallbackFunction* aCallback,
|
||||
CustomElementData* aOwnerData);
|
||||
void Traverse(nsCycleCollectionTraversalCallback& aCb) const;
|
||||
void Call();
|
||||
void SetArgs(LifecycleCallbackArgs& aArgs)
|
||||
{
|
||||
MOZ_ASSERT(mType == nsIDocument::eAttributeChanged,
|
||||
"Arguments are only used by attribute changed callback.");
|
||||
mArgs = aArgs;
|
||||
}
|
||||
|
||||
private:
|
||||
// The this value to use for invocation of the callback.
|
||||
RefPtr<mozilla::dom::Element> mThisObject;
|
||||
RefPtr<mozilla::dom::CallbackFunction> mCallback;
|
||||
// The type of callback (eCreated, eAttached, etc.)
|
||||
nsIDocument::ElementCallbackType mType;
|
||||
// Arguments to be passed to the callback,
|
||||
// used by the attribute changed callback.
|
||||
LifecycleCallbackArgs mArgs;
|
||||
// CustomElementData that contains this callback in the
|
||||
// callback queue.
|
||||
CustomElementData* mOwnerData;
|
||||
};
|
||||
|
||||
// Each custom element has an associated callback queue and an element is
|
||||
// being created flag.
|
||||
struct CustomElementData
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(CustomElementData)
|
||||
|
||||
explicit CustomElementData(nsIAtom* aType);
|
||||
// Objects in this array are transient and empty after each microtask
|
||||
// checkpoint.
|
||||
nsTArray<nsAutoPtr<CustomElementCallback>> mCallbackQueue;
|
||||
// Custom element type, for <button is="x-button"> or <x-button>
|
||||
// this would be x-button.
|
||||
nsCOMPtr<nsIAtom> mType;
|
||||
// The callback that is next to be processed upon calling RunCallbackQueue.
|
||||
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.
|
||||
void RunCallbackQueue();
|
||||
|
||||
private:
|
||||
virtual ~CustomElementData() {}
|
||||
};
|
||||
|
||||
class Registry : public nsISupports
|
||||
{
|
||||
public:
|
||||
friend class ::nsDocument;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Registry)
|
||||
|
||||
Registry();
|
||||
|
||||
protected:
|
||||
virtual ~Registry();
|
||||
|
||||
typedef nsClassHashtable<mozilla::dom::CustomElementHashKey,
|
||||
mozilla::dom::CustomElementDefinition>
|
||||
DefinitionMap;
|
||||
typedef nsClassHashtable<mozilla::dom::CustomElementHashKey,
|
||||
nsTArray<nsWeakPtr>>
|
||||
CandidateMap;
|
||||
|
||||
// Hashtable for custom element definitions in web components.
|
||||
// Custom prototypes are stored in the compartment where
|
||||
// registerElement was called.
|
||||
DefinitionMap mCustomDefinitions;
|
||||
|
||||
// The "upgrade candidates map" from the web components spec. Maps from a
|
||||
// namespace id and local name to a list of elements to upgrade if that
|
||||
// element is registered as a custom element.
|
||||
CandidateMap mCandidatesMap;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@ -1218,36 +1117,6 @@ public:
|
||||
|
||||
virtual nsIDOMNode* AsDOMNode() override { return this; }
|
||||
|
||||
virtual void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition = nullptr) override;
|
||||
|
||||
static void ProcessTopElementQueue();
|
||||
|
||||
void GetCustomPrototype(int32_t aNamespaceID,
|
||||
nsIAtom* aAtom,
|
||||
JS::MutableHandle<JSObject*> prototype)
|
||||
{
|
||||
if (!mRegistry) {
|
||||
prototype.set(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::dom::CustomElementHashKey key(aNamespaceID, aAtom);
|
||||
mozilla::dom::CustomElementDefinition* definition;
|
||||
if (mRegistry->mCustomDefinitions.Get(&key, &definition)) {
|
||||
prototype.set(definition->mPrototype);
|
||||
} else {
|
||||
prototype.set(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static bool RegisterEnabled();
|
||||
|
||||
virtual nsresult RegisterUnresolvedElement(mozilla::dom::Element* aElement,
|
||||
nsIAtom* aTypeName = nullptr) override;
|
||||
|
||||
// WebIDL bits
|
||||
virtual mozilla::dom::DOMImplementation*
|
||||
GetImplementation(mozilla::ErrorResult& rv) override;
|
||||
@ -1268,7 +1137,6 @@ public:
|
||||
const nsAString& aQualifiedName,
|
||||
const mozilla::dom::ElementCreationOptions& aOptions,
|
||||
mozilla::ErrorResult& rv) override;
|
||||
virtual void UseRegistryFromDocument(nsIDocument* aDocument) override;
|
||||
|
||||
virtual nsIDocument* MasterDocument() override
|
||||
{
|
||||
@ -1280,7 +1148,6 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(master);
|
||||
mMasterDocument = master;
|
||||
UseRegistryFromDocument(mMasterDocument);
|
||||
}
|
||||
|
||||
virtual bool IsMasterDocument() override
|
||||
@ -1397,8 +1264,6 @@ public:
|
||||
// Set our title
|
||||
virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv) override;
|
||||
|
||||
static void XPCOMShutdown();
|
||||
|
||||
bool mIsTopLevelContentDocument: 1;
|
||||
bool mIsContentDocument: 1;
|
||||
|
||||
@ -1503,23 +1368,8 @@ protected:
|
||||
nsWeakPtr mFullscreenRoot;
|
||||
|
||||
private:
|
||||
// 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<mozilla::dom::CustomElementData>>> sProcessingStack;
|
||||
|
||||
static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
|
||||
|
||||
/**
|
||||
* Looking up a custom element definition.
|
||||
* https://html.spec.whatwg.org/#look-up-a-custom-element-definition
|
||||
*/
|
||||
mozilla::dom::CustomElementDefinition* LookupCustomElementDefinition(
|
||||
const nsAString& aLocalName, uint32_t aNameSpaceID, const nsAString* aIs);
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -1535,18 +1385,11 @@ private:
|
||||
ErrorResult& rv);
|
||||
|
||||
public:
|
||||
// Enqueue created callback or register upgrade candidate for
|
||||
// newly created custom elements, possibly extending an existing type.
|
||||
// ex. <x-button>, <button is="x-button> (type extension)
|
||||
virtual void SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
const nsAString* aTypeExtension) override;
|
||||
virtual already_AddRefed<mozilla::dom::CustomElementsRegistry>
|
||||
GetCustomElementsRegistry() override;
|
||||
|
||||
static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
|
||||
|
||||
// The "registry" from the web components spec.
|
||||
RefPtr<mozilla::dom::Registry> mRegistry;
|
||||
|
||||
RefPtr<mozilla::EventListenerManager> mListenerManager;
|
||||
RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
|
||||
RefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
|
||||
|
@ -2482,34 +2482,13 @@ public:
|
||||
|
||||
nsIDocument* GetTopLevelContentDocument();
|
||||
|
||||
/**
|
||||
* Registers an unresolved custom element that is a candidate for
|
||||
* upgrade when the definition is registered via registerElement.
|
||||
* |aTypeName| is the name of the custom element type, if it is not
|
||||
* provided, then element name is used. |aTypeName| should be provided
|
||||
* when registering a custom element that extends an existing
|
||||
* element. e.g. <button is="x-button">.
|
||||
*/
|
||||
virtual nsresult RegisterUnresolvedElement(Element* aElement,
|
||||
nsIAtom* aTypeName = nullptr) = 0;
|
||||
virtual void EnqueueLifecycleCallback(ElementCallbackType aType,
|
||||
Element* aCustomElement,
|
||||
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition = nullptr) = 0;
|
||||
virtual void SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
const nsAString* aTypeExtension = nullptr) = 0;
|
||||
virtual void
|
||||
RegisterElement(JSContext* aCx, const nsAString& aName,
|
||||
const mozilla::dom::ElementRegistrationOptions& aOptions,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
mozilla::ErrorResult& rv) = 0;
|
||||
|
||||
/**
|
||||
* In some cases, new document instances must be associated with
|
||||
* an existing web components custom element registry as specified.
|
||||
*/
|
||||
virtual void UseRegistryFromDocument(nsIDocument* aDocument) = 0;
|
||||
virtual already_AddRefed<mozilla::dom::CustomElementsRegistry>
|
||||
GetCustomElementsRegistry() = 0;
|
||||
|
||||
already_AddRefed<nsContentList>
|
||||
GetElementsByTagName(const nsAString& aTagName)
|
||||
|
@ -471,15 +471,14 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
// enqueing created callback and prototype swizzling.
|
||||
Element* elem = clone->AsElement();
|
||||
if (nsContentUtils::IsCustomElementName(nodeInfo->NameAtom())) {
|
||||
elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID());
|
||||
nsContentUtils::SetupCustomElement(elem);
|
||||
} else {
|
||||
// Check if node may be custom element by type extension.
|
||||
// ex. <button is="x-button">
|
||||
nsAutoString extension;
|
||||
if (elem->GetAttr(kNameSpaceID_None, nsGkAtoms::is, extension) &&
|
||||
!extension.IsEmpty()) {
|
||||
elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID(),
|
||||
&extension);
|
||||
nsContentUtils::SetupCustomElement(elem, &extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,14 +257,12 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
|
||||
if ((tag == eHTMLTag_userdefined &&
|
||||
nsContentUtils::IsCustomElementName(name)) ||
|
||||
aIs) {
|
||||
nsIDocument* doc = nodeInfo->GetDocument();
|
||||
|
||||
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
|
||||
if (!*aResult) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML, aIs);
|
||||
nsContentUtils::SetupCustomElement(*aResult, aIs);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -29,21 +29,6 @@ function createdCallbackFromMainDoc() {
|
||||
document.createElement("x-associated-doc-callback-elem");
|
||||
}
|
||||
|
||||
function createdCallbackFromAssociatedDoc() {
|
||||
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-main-doc-callback-elem", { prototype: proto });
|
||||
assocDoc.createElement("x-main-doc-callback-elem");
|
||||
}
|
||||
|
||||
function createdCallbackFromDocHTMLNamespace() {
|
||||
var createdCallbackCalled = false;
|
||||
var assocDoc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", null);
|
||||
@ -80,7 +65,6 @@ function runNextTest() {
|
||||
}
|
||||
|
||||
var testFunctions = [
|
||||
createdCallbackFromAssociatedDoc,
|
||||
createdCallbackFromMainDoc,
|
||||
createdCallbackFromDocHTMLNamespace,
|
||||
registerNoRegistryDoc,
|
||||
|
@ -116,10 +116,10 @@ using namespace mozilla::system;
|
||||
#include "nsPermissionManager.h"
|
||||
#include "nsCookieService.h"
|
||||
#include "nsApplicationCacheService.h"
|
||||
#include "mozilla/dom/CustomElementsRegistry.h"
|
||||
#include "mozilla/dom/time/DateCacheCleaner.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "nsDocument.h"
|
||||
#include "mozilla/dom/HTMLVideoElement.h"
|
||||
#include "CameraPreferences.h"
|
||||
#include "TouchManager.h"
|
||||
@ -437,7 +437,7 @@ nsLayoutStatics::Shutdown()
|
||||
|
||||
DisplayItemClip::Shutdown();
|
||||
|
||||
nsDocument::XPCOMShutdown();
|
||||
CustomElementsRegistry::XPCOMShutdown();
|
||||
|
||||
CacheObserver::Shutdown();
|
||||
|
||||
|
@ -440,9 +440,7 @@ nsHtml5TreeOperation::CreateElement(int32_t aNs,
|
||||
|
||||
// Custom element setup may be needed if there is an "is" attribute.
|
||||
if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) {
|
||||
newContent->OwnerDoc()->SetupCustomElement(newContent,
|
||||
newContent->GetNameSpaceID(),
|
||||
&value);
|
||||
nsContentUtils::SetupCustomElement(newContent, &value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user