Skip to content

Commit cddd51d

Browse files
keithamuskcirkel@mozilla.com
authored andcommitted
Bug 2018900 - Implement customElementRegistry on Element DocumentOrShadowRoot r=dom-core,webidl,smaug
Differential Revision: https://phabricator.services.mozilla.com/D285678
1 parent 0c85c41 commit cddd51d

25 files changed

+327
-155
lines changed

dom/base/CustomElementRegistry.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "jsapi.h"
1212
#include "mozilla/AsyncEventDispatcher.h"
1313
#include "mozilla/AutoRestore.h"
14+
#include "mozilla/ClearOnShutdown.h"
1415
#include "mozilla/CycleCollectedJSContext.h"
1516
#include "mozilla/CycleCollectedUniquePtr.h"
1617
#include "mozilla/HoldDropJSObjects.h"
@@ -19,6 +20,7 @@
1920
#include "mozilla/dom/CustomElementRegistryBinding.h"
2021
#include "mozilla/dom/CustomEvent.h"
2122
#include "mozilla/dom/DocGroup.h"
23+
#include "mozilla/dom/Element.h"
2224
#include "mozilla/dom/ElementBinding.h"
2325
#include "mozilla/dom/HTMLElement.h"
2426
#include "mozilla/dom/HTMLElementBinding.h"
@@ -732,6 +734,42 @@ void CustomElementRegistry::EnqueueLifecycleCallback(
732734
reactionsStack->EnqueueCallbackReaction(aCustomElement, std::move(callback));
733735
}
734736

737+
using ScopedRegistryMap =
738+
nsRefPtrHashtable<nsPtrHashKey<nsINode>, CustomElementRegistry>;
739+
740+
static StaticAutoPtr<ScopedRegistryMap> gScopedRegistryMap;
741+
742+
/* static */
743+
already_AddRefed<CustomElementRegistry>
744+
CustomElementRegistry::GetScopedRegistry(nsINode& aNode) {
745+
if (!gScopedRegistryMap) {
746+
return nullptr;
747+
}
748+
RefPtr<CustomElementRegistry> registry = gScopedRegistryMap->Get(&aNode);
749+
if (registry) {
750+
return registry.forget();
751+
}
752+
return nullptr;
753+
}
754+
755+
/* static */
756+
void CustomElementRegistry::SetScopedRegistry(
757+
nsINode& aNode, CustomElementRegistry& aRegistry) {
758+
MOZ_ASSERT(aRegistry.IsScoped());
759+
if (!gScopedRegistryMap) {
760+
gScopedRegistryMap = new ScopedRegistryMap();
761+
ClearOnShutdown(&gScopedRegistryMap);
762+
}
763+
gScopedRegistryMap->InsertOrUpdate(&aNode, &aRegistry);
764+
}
765+
766+
/* static */
767+
void CustomElementRegistry::RemoveScopedRegistry(nsINode& aNode) {
768+
if (gScopedRegistryMap) {
769+
gScopedRegistryMap->Remove(&aNode);
770+
}
771+
}
772+
735773
namespace {
736774

737775
class CandidateFinder {

dom/base/CustomElementRegistry.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ class CustomElementRegistry final : public nsISupports, public nsWrapperCache {
471471

472472
bool IsScoped() const { return mIsScoped; }
473473

474+
static already_AddRefed<CustomElementRegistry> GetScopedRegistry(nsINode&);
475+
static void SetScopedRegistry(nsINode&, CustomElementRegistry&);
476+
static void RemoveScopedRegistry(nsINode&);
477+
474478
void TraceDefinitions(JSTracer* aTrc);
475479

476480
private:

dom/base/Document.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
#include "mozilla/dom/CloseWatcherManager.h"
156156
#include "mozilla/dom/Comment.h"
157157
#include "mozilla/dom/ContentChild.h"
158+
#include "mozilla/dom/CustomElementRegistry.h"
158159
#include "mozilla/dom/DOMImplementation.h"
159160
#include "mozilla/dom/DOMIntersectionObserver.h"
160161
#include "mozilla/dom/DOMStringList.h"
@@ -11937,6 +11938,18 @@ void Document::TerminateParserAndDisableScripts() {
1193711938
}
1193811939
}
1193911940

11941+
/* https://dom.spec.whatwg.org/#effective-global-custom-element-registry */
11942+
CustomElementRegistry* Document::GetEffectiveGlobalCustomElementRegistry() {
11943+
// 1. If document's custom element registry is a global custom element
11944+
// registry, then return document's custom element registry.
11945+
CustomElementRegistry* registry = GetCustomElementRegistry();
11946+
if (registry && !registry->IsScoped()) {
11947+
return registry;
11948+
}
11949+
// 2. Return null.
11950+
return nullptr;
11951+
}
11952+
1194011953
already_AddRefed<Element> Document::CreateElem(const nsAString& aName,
1194111954
nsAtom* aPrefix,
1194211955
int32_t aNamespaceID,

dom/base/Document.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,6 +2368,10 @@ class Document : public nsINode,
23682368
int32_t aNamespaceID,
23692369
const nsAString* aIs = nullptr);
23702370

2371+
// https://dom.spec.whatwg.org/#effective-global-custom-element-registry
2372+
mozilla::dom::CustomElementRegistry*
2373+
GetEffectiveGlobalCustomElementRegistry();
2374+
23712375
/**
23722376
* Get the security info (i.e. SSL state etc) that the document got
23732377
* from the channel/document that created the content of the

dom/base/DocumentOrShadowRoot.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "mozilla/PresShell.h"
1313
#include "mozilla/StyleSheet.h"
1414
#include "mozilla/dom/AnimatableBinding.h"
15+
#include "mozilla/dom/CustomElementRegistry.h"
1516
#include "mozilla/dom/Document.h"
1617
#include "mozilla/dom/HTMLInputElement.h"
1718
#include "mozilla/dom/ShadowRoot.h"
@@ -756,4 +757,46 @@ void DocumentOrShadowRoot::Unlink(DocumentOrShadowRoot* tmp) {
756757
tmp->mIdentifierMap.Clear();
757758
}
758759

760+
void DocumentOrShadowRoot::SetCustomElementRegistry(
761+
CustomElementRegistry& aRegistry) {
762+
MOZ_ASSERT(StaticPrefs::dom_scoped_custom_element_registries_enabled());
763+
MOZ_ASSERT(mKind == Kind::ShadowRoot,
764+
"SetCustomElementRegistry should only be called on ShadowRoots");
765+
ShadowRoot& root = static_cast<ShadowRoot&>(AsNode());
766+
root.SetCustomElementRegistry(&aRegistry);
767+
}
768+
769+
/* https://dom.spec.whatwg.org/#dom-documentorshadowroot-customelementregistry
770+
*/
771+
CustomElementRegistry* DocumentOrShadowRoot::GetCustomElementRegistry() {
772+
// Step 1. If this is a document, then return this's custom element registry.
773+
// TODO(2021247): Per document registries
774+
if (mKind == Kind::Document) {
775+
Document* doc = AsNode().AsDocument();
776+
nsPIDOMWindowInner* window = doc->GetInnerWindow();
777+
if (!window) {
778+
return nullptr;
779+
}
780+
return window->CustomElements();
781+
}
782+
783+
// Step 2. Assert: this is a ShadowRoot node.
784+
MOZ_ASSERT(AsNode().IsShadowRoot());
785+
786+
// Step 3. Return this's custom element registry.
787+
ShadowRoot* root = ShadowRoot::FromNode(AsNode());
788+
MOZ_ASSERT(root);
789+
if (StaticPrefs::dom_scoped_custom_element_registries_enabled()) {
790+
return root->GetCustomElementRegistry();
791+
}
792+
793+
// XXX Fallback when scoped registries are disabled: return the window's
794+
// global registry.
795+
nsPIDOMWindowInner* window = root->OwnerDoc()->GetInnerWindow();
796+
if (!window) {
797+
return nullptr;
798+
}
799+
return window->CustomElements();
800+
}
801+
759802
} // namespace mozilla::dom

dom/base/DocumentOrShadowRoot.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace dom {
3131

3232
class Animation;
3333
class Element;
34+
class CustomElementRegistry;
3435
class Document;
3536
class DocumentOrShadowRoot;
3637
class HTMLInputElement;
@@ -233,6 +234,10 @@ class DocumentOrShadowRoot {
233234
}
234235
}
235236

237+
// https://dom.spec.whatwg.org/#dom-documentorshadowroot-customelementregistry
238+
CustomElementRegistry* GetCustomElementRegistry();
239+
void SetCustomElementRegistry(CustomElementRegistry&);
240+
236241
protected:
237242
// Cycle collection helper functions
238243
void TraverseSheetRefInStylesIfApplicable(

dom/base/Element.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,29 @@ int32_t Element::TabIndex() {
499499
return TabIndexDefault();
500500
}
501501

502+
/* static */
503+
void Element::TraverseCustomElementRegistry(
504+
Element* aElement, nsCycleCollectionTraversalCallback& aCb) {
505+
if (aElement->GetCustomElementRegistryState() ==
506+
CustomElementRegistryState::Scoped) {
507+
RefPtr<CustomElementRegistry> registry =
508+
CustomElementRegistry::GetScopedRegistry(*aElement);
509+
if (registry) {
510+
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "scoped CustomElementRegistry");
511+
aCb.NoteXPCOMChild(registry.get());
512+
}
513+
}
514+
}
515+
516+
/* static */
517+
void Element::UnlinkCustomElementRegistry(Element* aElement) {
518+
if (aElement->GetCustomElementRegistryState() ==
519+
CustomElementRegistryState::Scoped) {
520+
CustomElementRegistry::RemoveScopedRegistry(*aElement);
521+
aElement->SetCustomElementRegistryState(CustomElementRegistryState::Global);
522+
}
523+
}
524+
502525
void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
503526
ErrorResult& aError) {
504527
const RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
@@ -538,6 +561,41 @@ void Element::SetShadowRoot(ShadowRoot* aShadowRoot) {
538561
slots->mShadowRoot = aShadowRoot;
539562
}
540563

564+
void Element::SetCustomElementRegistry(
565+
CustomElementRegistry* aCustomElementRegistry) {
566+
MOZ_ASSERT(StaticPrefs::dom_scoped_custom_element_registries_enabled());
567+
MOZ_ASSERT(!!aCustomElementRegistry,
568+
"We shouldn't be setting a null custom element registry");
569+
MOZ_ASSERT(
570+
GetCustomElementRegistryState() != CustomElementRegistryState::Scoped,
571+
"We shouldn't override an already assigned scoped registry");
572+
573+
if (aCustomElementRegistry->IsScoped()) {
574+
SetCustomElementRegistryState(CustomElementRegistryState::Scoped);
575+
CustomElementRegistry::SetScopedRegistry(*this, *aCustomElementRegistry);
576+
} else {
577+
SetCustomElementRegistryState(CustomElementRegistryState::Global);
578+
}
579+
}
580+
581+
/* https://dom.spec.whatwg.org/#element-custom-element-registry */
582+
CustomElementRegistry* Element::GetCustomElementRegistry() {
583+
switch (GetCustomElementRegistryState()) {
584+
case CustomElementRegistryState::Global:
585+
return OwnerDoc()->GetEffectiveGlobalCustomElementRegistry();
586+
case CustomElementRegistryState::Null:
587+
return nullptr;
588+
case CustomElementRegistryState::Scoped: {
589+
RefPtr<CustomElementRegistry> registry =
590+
CustomElementRegistry::GetScopedRegistry(*this);
591+
MOZ_ASSERT(registry);
592+
return registry;
593+
}
594+
}
595+
MOZ_ASSERT_UNREACHABLE("Invalid CustomElementRegistryState");
596+
return nullptr;
597+
}
598+
541599
void Element::SetLastRememberedBSize(float aBSize) {
542600
ExtendedDOMSlots()->mLastRememberedBSize = Some(aBSize);
543601
}
@@ -4515,6 +4573,25 @@ nsresult Element::CopyInnerTo(Element* aDst, ReparseAttributes aReparse) {
45154573
}
45164574
}
45174575

4576+
// https://dom.spec.whatwg.org/#clone-a-single-node
4577+
// Step 2.1. Let registry be node's custom element registry.
4578+
// Step 2.2. If registry is null, then set registry to fallbackRegistry.
4579+
// Step 2.3. If registry is a global custom element registry, then set
4580+
// registry to document's effective global custom element registry.
4581+
// XXX Steps 2.1-2.3 are partially handled here by propagating registry
4582+
// state; the full registry resolution happens in "create an element".
4583+
CustomElementRegistryState state = GetCustomElementRegistryState();
4584+
if (state == CustomElementRegistryState::Scoped) {
4585+
MOZ_ASSERT(StaticPrefs::dom_scoped_custom_element_registries_enabled());
4586+
RefPtr<CustomElementRegistry> scopedRegistry =
4587+
CustomElementRegistry::GetScopedRegistry(*this);
4588+
aDst->SetCustomElementRegistry(scopedRegistry);
4589+
} else {
4590+
MOZ_ASSERT(state == CustomElementRegistryState::Global ||
4591+
StaticPrefs::dom_scoped_custom_element_registries_enabled());
4592+
aDst->SetCustomElementRegistryState(state);
4593+
}
4594+
45184595
// https://html.spec.whatwg.org/#enqueue-a-custom-element-upgrade-reaction
45194596
dom::NodeInfo* dstNodeInfo = aDst->NodeInfo();
45204597
if (CustomElementData* data = GetCustomElementData()) {

dom/base/Element.h

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,32 @@ enum : uint32_t {
204204
// Document::mContentIdentifiersForLCP.
205205
ELEMENT_IN_CONTENT_IDENTIFIER_FOR_LCP = ELEMENT_FLAG_BIT(7),
206206

207+
// 2-bit field encoding the element's custom element registry state.
208+
// See CustomElementRegistryState for the possible values.
209+
ELEMENT_CUSTOM_ELEMENT_REGISTRY_LOW_BIT = ELEMENT_FLAG_BIT(8),
210+
ELEMENT_CUSTOM_ELEMENT_REGISTRY_MASK =
211+
ELEMENT_FLAG_BIT(8) | ELEMENT_FLAG_BIT(9),
212+
207213
// Remaining bits are for subclasses
208-
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 8
214+
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 10
209215
};
210216

211217
#undef ELEMENT_FLAG_BIT
212218

213219
// Make sure we have space for our bits
214220
ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
215221

222+
// Encodes the custom element registry state for an element or shadow root.
223+
// Global: uses the owner document's effective global registry (initial
224+
// state).
225+
// Null: explicitly opted out of all registries.
226+
// Scoped: uses a scoped registry stored in gScopedRegistryMap.
227+
enum class CustomElementRegistryState : uint8_t {
228+
Global = 0,
229+
Null = 1,
230+
Scoped = 2,
231+
};
232+
216233
namespace mozilla {
217234
enum class PseudoStyleType : uint8_t;
218235
struct PseudoStyleRequest;
@@ -296,6 +313,7 @@ class Element : public FragmentOrElement {
296313

297314
~Element() {
298315
NS_ASSERTION(!HasServoData(), "expected ServoData to be cleared earlier");
316+
UnlinkCustomElementRegistry(this);
299317
}
300318

301319
#endif // MOZILLA_INTERNAL_API
@@ -1649,6 +1667,32 @@ class Element : public FragmentOrElement {
16491667
Element* ResolveReferenceTarget() const;
16501668
Element* RetargetReferenceTargetForBindings(Element* aElement) const;
16511669

1670+
CustomElementRegistryState GetCustomElementRegistryState() const {
1671+
return static_cast<CustomElementRegistryState>(
1672+
(GetFlags() & ELEMENT_CUSTOM_ELEMENT_REGISTRY_MASK) /
1673+
ELEMENT_CUSTOM_ELEMENT_REGISTRY_LOW_BIT);
1674+
}
1675+
1676+
void SetCustomElementRegistryState(CustomElementRegistryState aState) {
1677+
UnsetFlags(ELEMENT_CUSTOM_ELEMENT_REGISTRY_MASK);
1678+
SetFlags(static_cast<uint32_t>(aState) *
1679+
ELEMENT_CUSTOM_ELEMENT_REGISTRY_LOW_BIT);
1680+
}
1681+
1682+
// Returns true if the element has an explicit registry set.
1683+
// That registry might be null.
1684+
bool HasCustomElementRegistry() const {
1685+
return GetCustomElementRegistryState() !=
1686+
CustomElementRegistryState::Global;
1687+
}
1688+
1689+
// https://dom.spec.whatwg.org/#element-custom-element-registry
1690+
CustomElementRegistry* GetCustomElementRegistry();
1691+
void SetCustomElementRegistry(CustomElementRegistry* aCustomElementRegistry);
1692+
static void TraverseCustomElementRegistry(
1693+
Element* aElement, nsCycleCollectionTraversalCallback& aCb);
1694+
static void UnlinkCustomElementRegistry(Element* aElement);
1695+
16521696
const Maybe<float> GetLastRememberedBSize() const {
16531697
const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
16541698
return slots ? slots->mLastRememberedBSize : Nothing();

dom/base/FragmentOrElement.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
13841384
tmp->ExtendedDOMSlots()->mShadowRoot = nullptr;
13851385
}
13861386

1387+
if (tmp->IsElement()) {
1388+
Element::UnlinkCustomElementRegistry(tmp->AsElement());
1389+
}
1390+
13871391
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
13881392

13891393
void FragmentOrElement::MarkNodeChildren(nsINode* aNode) {
@@ -1827,6 +1831,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
18271831
NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
18281832
}
18291833
}
1834+
Element::TraverseCustomElementRegistry(element, cb);
18301835
}
18311836
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
18321837

0 commit comments

Comments
 (0)