Skip to content

Commit

Permalink
Bug 1275835 - Part 1: Move custom element codes from nsDocument to Cu…
Browse files Browse the repository at this point in the history
…stomElementsRegistry; r=wchen

MozReview-Commit-ID: 9gTSFrYW7o3

--HG--
extra : rebase_source : f123f21aadaa18641ddd7fa7fa67eb27a4152f83
  • Loading branch information
EdgarChen committed Aug 30, 2016
1 parent 5a59692 commit 6794685
Show file tree
Hide file tree
Showing 15 changed files with 755 additions and 690 deletions.
416 changes: 411 additions & 5 deletions dom/base/CustomElementsRegistry.cpp

Large diffs are not rendered by default.

137 changes: 136 additions & 1 deletion dom/base/CustomElementsRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -101,20 +174,82 @@ 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)

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;

Expand Down
5 changes: 0 additions & 5 deletions dom/base/DOMImplementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
16 changes: 10 additions & 6 deletions dom/base/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions dom/base/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class EventStateManager;
namespace dom {

class Animation;
class CustomElementsRegistry;
class Link;
class UndoManager;
class DOMRect;
Expand Down Expand Up @@ -426,6 +427,9 @@ class Element : public FragmentOrElement
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;

Expand Down
121 changes: 121 additions & 0 deletions dom/base/nsContentUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
Loading

0 comments on commit 6794685

Please sign in to comment.