Closed Bug 1331334 Opened 4 years ago Closed 2 years ago

Implement :defined pseudo-class for custom elements

Categories

(Core :: DOM: Core & HTML, defect, P2)

defect

Tracking

()

RESOLVED FIXED
mozilla63
Tracking Status
firefox63 --- fixed

People

(Reporter: edgar, Assigned: smaug)

References

()

Details

(Keywords: dev-doc-complete, Whiteboard: dom-ce-m4)

Attachments

(1 file, 2 obsolete files)

Whiteboard: dom-ce-m3
Priority: -- → P3
Assignee: nobody → jdai
Status: NEW → ASSIGNED
Depends on: 1299363, 1301024
Whiteboard: dom-ce-m3 → dom-ce-m4
Look like Google Chrome and Safari both support :defined pseudo-class, http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=5680, we probably need this before shipping Custom Elemenets, so set priority to P2. Feel free to change back if you think it is not necessary to be a P2 bug.
Priority: P3 → P2
Depends on: 1417829
Attached patch patch, v1 (obsolete) — Splinter Review
Patch for implementing :defined pseudo-class for custom elements, still working on web-platform tests.
Comment on attachment 8942579 [details] [diff] [review]
patch, v1

Review of attachment 8942579 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/base/CustomElementRegistry.cpp
@@ +956,5 @@
>    // Step 6 and step 7.
>    DoUpgrade(aElement, aDefinition->mConstructor, aRv);
>    if (aRv.Failed()) {
>      data->mState = CustomElementData::State::eFailed;
> +    aElement->RemoveStatesSilently(NS_EVENT_STATE_DEFINED);

Why we need to remove NS_EVENT_STATE_DEFINED here? Is it possible that we add NS_EVENT_STATE_DEFINED on a "undefined" element?

::: dom/base/Element.cpp
@@ +1730,5 @@
>        }
> +    } else {
> +      // An element whose custom element state is "uncustomized" is said
> +      // to be defined. https://dom.spec.whatwg.org/#concept-element-defined
> +      AddStatesSilently(NS_EVENT_STATE_DEFINED);

It seems not necessary to set the state for every BindToTree call for "uncustomized" case, we probably could set the state at the element creation time, I mean in nsContentUtils::NewXULOrHTMLElement().

@@ +4408,5 @@
> +  // https://dom.spec.whatwg.org/#concept-element-defined
> +  if (aData->mState == CustomElementData::State::eCustom) {
> +    AddStatesSilently(NS_EVENT_STATE_DEFINED);
> +  } else {
> +    RemoveStatesSilently(NS_EVENT_STATE_DEFINED);

Similar question here, is it possible that we add NS_EVENT_STATE_DEFINED on a element which state is not "custom"?

::: dom/events/EventStates.h
@@ +243,5 @@
>  #define NS_EVENT_STATE_REQUIRED      NS_DEFINE_EVENT_STATE_MACRO(21)
>  // Content is optional (and can be required).
>  #define NS_EVENT_STATE_OPTIONAL      NS_DEFINE_EVENT_STATE_MACRO(22)
> +// Element is a defined custom element
> +#define NS_EVENT_STATE_DEFINED NS_DEFINE_EVENT_STATE_MACRO(23)

Nit: Align NS_DEFINE_EVENT_STATE_MACRO with previous one.

@@ +360,5 @@
>    NS_EVENT_STATE_FOCUSRING |                  \
>    NS_EVENT_STATE_FOCUS_WITHIN |               \
>    NS_EVENT_STATE_FULL_SCREEN |                \
>    NS_EVENT_STATE_HOVER |                      \
> +  NS_EVENT_STATE_DEFINED |                    \

Nit: List it in alphabetical order?

::: layout/style/nsCSSPseudoClassList.h
@@ +89,5 @@
>  // Match nodes that are HTML but not XHTML
>  CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "")
>  
> +// Match all custom elements that is defined.
> + CSS_STATE_PSEUDO_CLASS(defined, ":defined", 0, "", NS_EVENT_STATE_DEFINED)

We should put :defined behind the "dom.webcomponent.customelements.enable" pref.
Comment on attachment 8942579 [details] [diff] [review]
patch, v1

Review of attachment 8942579 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/base/CustomElementRegistry.cpp
@@ +964,5 @@
>    }
>  
>    // Step 8.
>    data->mState = CustomElementData::State::eCustom;
> +  aElement->AddStatesSilently(NS_EVENT_STATE_DEFINED);

Do we need to use AddStates() to notify that the state is changed so that the :define rule can be applied once the element is upgraded?
(In reply to Edgar Chen [:edgar] from comment #3)
> Comment on attachment 8942579 [details] [diff] [review]
> patch, v1
> 
> Review of attachment 8942579 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/base/CustomElementRegistry.cpp
> @@ +956,5 @@
> >    // Step 6 and step 7.
> >    DoUpgrade(aElement, aDefinition->mConstructor, aRv);
> >    if (aRv.Failed()) {
> >      data->mState = CustomElementData::State::eFailed;
> > +    aElement->RemoveStatesSilently(NS_EVENT_STATE_DEFINED);
> 
> Why we need to remove NS_EVENT_STATE_DEFINED here? Is it possible that we
> add NS_EVENT_STATE_DEFINED on a "undefined" element?
> 
I was considering clone a built-in custom elements, and we change its is attribute to upgrade fail, then :defined state will be removed. But it looks like state will not be cloned to a new node. I'll remove this change. Thank you.

> ::: dom/base/Element.cpp
> @@ +1730,5 @@
> >        }
> > +    } else {
> > +      // An element whose custom element state is "uncustomized" is said
> > +      // to be defined. https://dom.spec.whatwg.org/#concept-element-defined
> > +      AddStatesSilently(NS_EVENT_STATE_DEFINED);
> 
> It seems not necessary to set the state for every BindToTree call for
> "uncustomized" case, we probably could set the state at the element creation
> time, I mean in nsContentUtils::NewXULOrHTMLElement().
> 
I agree set the state at the element creation time, we should also consider set the state for parser created normal element at nsHtml5TreeOperation::CreateHTMLElement[1]. The nsContentUtils::NewXULOrHTMLElement is a case for createElement which custom element state is "uncustomized".

[1] https://searchfox.org/mozilla-central/rev/41925c0b6c6d58578690121de439d2a8d3d690f3/parser/html/nsHtml5TreeOperation.cpp#464

> @@ +4408,5 @@
> > +  // https://dom.spec.whatwg.org/#concept-element-defined
> > +  if (aData->mState == CustomElementData::State::eCustom) {
> > +    AddStatesSilently(NS_EVENT_STATE_DEFINED);
> > +  } else {
> > +    RemoveStatesSilently(NS_EVENT_STATE_DEFINED);
> 
> Similar question here, is it possible that we add NS_EVENT_STATE_DEFINED on
> a element which state is not "custom"?
> 
I'll remove this change. Thank you.

> ::: dom/events/EventStates.h
> @@ +243,5 @@
> >  #define NS_EVENT_STATE_REQUIRED      NS_DEFINE_EVENT_STATE_MACRO(21)
> >  // Content is optional (and can be required).
> >  #define NS_EVENT_STATE_OPTIONAL      NS_DEFINE_EVENT_STATE_MACRO(22)
> > +// Element is a defined custom element
> > +#define NS_EVENT_STATE_DEFINED NS_DEFINE_EVENT_STATE_MACRO(23)
> 
> Nit: Align NS_DEFINE_EVENT_STATE_MACRO with previous one.
> 
Will do.

> @@ +360,5 @@
> >    NS_EVENT_STATE_FOCUSRING |                  \
> >    NS_EVENT_STATE_FOCUS_WITHIN |               \
> >    NS_EVENT_STATE_FULL_SCREEN |                \
> >    NS_EVENT_STATE_HOVER |                      \
> > +  NS_EVENT_STATE_DEFINED |                    \
> 
> Nit: List it in alphabetical order?
> 
Will do.

> ::: layout/style/nsCSSPseudoClassList.h
> @@ +89,5 @@
> >  // Match nodes that are HTML but not XHTML
> >  CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "")
> >  
> > +// Match all custom elements that is defined.
> > + CSS_STATE_PSEUDO_CLASS(defined, ":defined", 0, "", NS_EVENT_STATE_DEFINED)
> 
> We should put :defined behind the "dom.webcomponent.customelements.enable"
> pref.
Will do.

(In reply to Edgar Chen [:edgar] from comment #4)
> Comment on attachment 8942579 [details] [diff] [review]
> patch, v1
> 
> Review of attachment 8942579 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/base/CustomElementRegistry.cpp
> @@ +964,5 @@
> >    }
> >  
> >    // Step 8.
> >    data->mState = CustomElementData::State::eCustom;
> > +  aElement->AddStatesSilently(NS_EVENT_STATE_DEFINED);
> 
> Do we need to use that the state is changed so that
> the :define rule can be applied once the element is upgraded?
It seems no need to use AddStates() to notify, because states passed for AddStates must only be EXTERNALLY_MANAGED_STATES[1][2]. However, NS_EVENT_STATE_DEFINED isn't a EXTERNALLY_MANAGED_STATES.

[1] https://searchfox.org/mozilla-central/rev/41925c0b6c6d58578690121de439d2a8d3d690f3/dom/base/Element.h#664-674
[2] https://searchfox.org/mozilla-central/rev/41925c0b6c6d58578690121de439d2a8d3d690f3/dom/events/EventStates.h#347-364
(In reply to John Dai[:jdai] from comment #5)
> The nsContentUtils::NewXULOrHTMLElement is a case for createElement which custom
> element state is "uncustomized".
I guess you meant to say "custom element state is *not* "uncustomized""?


> It seems no need to use AddStates() to notify, because states passed for
> AddStates must only be EXTERNALLY_MANAGED_STATES.
But NS_EVENT_STATE_DEFINED is marked as EXTERNALLY_MANAGED_STATES in this patch.
(In reply to Edgar Chen [:edgar] from comment #6)
> > It seems no need to use AddStates() to notify, because states passed for
> > AddStates must only be EXTERNALLY_MANAGED_STATES.
> But NS_EVENT_STATE_DEFINED is marked as EXTERNALLY_MANAGED_STATES in this
> patch.
And using AddStatesSilently() without notifying probably won't handle certain circumstances well, something like http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=5741.
(In reply to Edgar Chen [:edgar] from comment #6)
> (In reply to John Dai[:jdai] from comment #5)
> > The nsContentUtils::NewXULOrHTMLElement is a case for createElement which custom
> > element state is "uncustomized".
> I guess you meant to say "custom element state is *not* "uncustomized""?
> 
Yes, I am trying to said custom element state is *not* "uncustomized".

> 
> > It seems no need to use AddStates() to notify, because states passed for
> > AddStates must only be EXTERNALLY_MANAGED_STATES.
> But NS_EVENT_STATE_DEFINED is marked as EXTERNALLY_MANAGED_STATES in this
> patch.
Yes, it's necessary to use AddStates() to notify.

(In reply to Edgar Chen [:edgar] from comment #7)
> (In reply to Edgar Chen [:edgar] from comment #6)
> > > It seems no need to use AddStates() to notify, because states passed for
> > > AddStates must only be EXTERNALLY_MANAGED_STATES.
> > But NS_EVENT_STATE_DEFINED is marked as EXTERNALLY_MANAGED_STATES in this
> > patch.
> And using AddStatesSilently() without notifying probably won't handle
> certain circumstances well, something like
> http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=5741.
Thanks for providing a test.
Attached patch patch, v2 (obsolete) — Splinter Review
Attachment #8942579 - Attachment is obsolete: true
(In reply to John Dai[:jdai] from comment #8)
> (In reply to Edgar Chen [:edgar] from comment #6)
> > (In reply to John Dai[:jdai] from comment #5)
> > > The nsContentUtils::NewXULOrHTMLElement is a case for createElement which custom
> > > element state is "uncustomized".
> > I guess you meant to say "custom element state is *not* "uncustomized""?
> > 
> Yes, I am trying to said custom element state is *not* "uncustomized".
>
Err...I revise my comment, we should also handle created a normal element from document.createElement which state should be "uncustomized", the code patch will go into nsContentUtils::NewXULOrHTMLElement. Sorry for not comment clearly.
(In reply to John Dai[:jdai] from comment #5)
> we should also consider set the state for parser created normal element at
> nsHtml5TreeOperation::CreateHTMLElement[1].

And also the cloneNode case around https://searchfox.org/mozilla-central/rev/1f9b4f0f0653b129b4db1b9fc5e9068054afaac0/dom/base/nsNodeUtils.cpp#492-525?
(In reply to Edgar Chen [:edgar] from comment #11)
> (In reply to John Dai[:jdai] from comment #5)
> > we should also consider set the state for parser created normal element at
> > nsHtml5TreeOperation::CreateHTMLElement[1].
> 
> And also the cloneNode case around
> https://searchfox.org/mozilla-central/rev/
> 1f9b4f0f0653b129b4db1b9fc5e9068054afaac0/dom/base/nsNodeUtils.cpp#492-525?

Yes, Not only we need to consider cloneNode case, but also createElementNS for not HTML namespace[1], because an element's namespace is not HTML, it should be "uncustomized"; i.e., "defined"[2].

[1] https://infra.spec.whatwg.org/#namespaces
[2] https://dom.spec.whatwg.org/#concept-create-element (Step 7.2)
Assignee: jdai → nobody
Status: ASSIGNED → NEW
Need to change the fix for bug 1441029. Basically remove the assertion, since random 'is' values are allowed.
this needs a stylo review, and the DOM part is style-related anyhow....

remote: View your changes here:
remote:   https://hg.mozilla.org/try/rev/9a6b65e5c4a1f76ed65894f5e0c46d33bed3d1f9
remote:   https://hg.mozilla.org/try/rev/e49a83d2ead94511a86cfa3e9a870704e10eb39c
remote: 
remote: Follow the progress of your build on Treeherder:
remote:   https://treeherder.mozilla.org/#/jobs?repo=try&revision=e49a83d2ead94511a86cfa3e9a870704e10eb39c
remote: recorded changegroup in replication log in 0.064s


In theory the state could be intrinsic, but since calculating that is a tad slow (because of several indirect accesses), went with EXTERNALLY_MANAGED_STATES
Assignee: nobody → bugs
Attachment #8943105 - Attachment is obsolete: true
Attachment #8988275 - Flags: review?(emilio)
Comment on attachment 8988275 [details] [diff] [review]
defined_pseudoclass.diff

Review of attachment 8988275 [details] [diff] [review]:
-----------------------------------------------------------------

r=me, thanks!

::: dom/base/CustomElementRegistry.cpp
@@ +1149,5 @@
>    if (aRv.Failed()) {
>      data->mState = CustomElementData::State::eFailed;
>      // Empty element's custom element reaction queue.
>      data->mReactionQueue.Clear();
> +    aElement->SetDefined(false);

If data was in eCustom state, we'd have bailed out early. I guess this is needed for is=""? If so, a comment would be nice.

::: dom/base/Element.cpp
@@ +4274,5 @@
>  {
>    SetHasCustomElementData();
> +
> +  if (aData->mState != CustomElementData::State::eCustom) {
> +    SetDefined(false);

This doesn't really have to notify either it seems.

::: dom/base/nsContentUtils.cpp
@@ +10024,5 @@
>            NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
>          } else {
>            NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
>          }
> +        (*aResult)->SetDefined(false);

This doesn't really need to notify, right? Not that we'd do anything, but saves a bunch of pointer-chases and virtual calls.
Attachment #8988275 - Flags: review?(emilio) → review+
(In reply to Emilio Cobos Álvarez [:emilio] from comment #15)

> ::: dom/base/CustomElementRegistry.cpp
> @@ +1149,5 @@
> >    if (aRv.Failed()) {
> >      data->mState = CustomElementData::State::eFailed;
> >      // Empty element's custom element reaction queue.
> >      data->mReactionQueue.Clear();
> > +    aElement->SetDefined(false);
> 
> If data was in eCustom state, we'd have bailed out early. I guess this is
> needed for is=""? If so, a comment would be nice.
er, actually  this shouldn't be needed

> > +  if (aData->mState != CustomElementData::State::eCustom) {
> > +    SetDefined(false);
> 
> This doesn't really have to notify either it seems.
It doesn't notify, because we aren't in document.

> This doesn't really need to notify, right? Not that we'd do anything, but
> saves a bunch of pointer-chases and virtual calls.
Doesn't notify, since we aren't in document.
I'm just trying to have sane API. The current state handling is rather error prone.
Pushed by opettay@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/b32be80ba883
Implement :defined pseudo-class for custom elements, r=emilio
Blocks: 1471871
https://hg.mozilla.org/mozilla-central/rev/b32be80ba883
Status: NEW → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla63
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.