mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-04 23:19:27 +00:00
LibWeb: Refactor Element.attachShadow() after spec changes
The bulk of this function is moved to a new "attach a shadow root" helper, designed to be used both from here and the HTML parser. (cherry picked from commit 043ad0eb7644a529c1ffbd4de9a4b89771808c84, and amended to make `is_valid_shadow_host_name()` static.
This commit is contained in:
parent
3a2260b28b
commit
34a4b28b89
|
@ -593,25 +593,33 @@ DOMTokenList* Element::class_list()
|
||||||
return m_class_list;
|
return m_class_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-element-attachshadow
|
// https://dom.spec.whatwg.org/#valid-shadow-host-name
|
||||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<ShadowRoot>> Element::attach_shadow(ShadowRootInit init)
|
static bool is_valid_shadow_host_name(FlyString const& name)
|
||||||
{
|
{
|
||||||
// 1. If this’s namespace is not the HTML namespace, then throw a "NotSupportedError" DOMException.
|
// A valid shadow host name is:
|
||||||
|
// - a valid custom element name
|
||||||
|
// - "article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p", "section", or "span"
|
||||||
|
if (!HTML::is_valid_custom_element_name(name)
|
||||||
|
&& !name.is_one_of("article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p", "section", "span")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#concept-attach-a-shadow-root
|
||||||
|
WebIDL::ExceptionOr<void> Element::attach_a_shadow_root(Bindings::ShadowRootMode mode, bool clonable, bool serializable, bool delegates_focus, Bindings::SlotAssignmentMode slot_assignment)
|
||||||
|
{
|
||||||
|
// 1. If element’s namespace is not the HTML namespace, then throw a "NotSupportedError" DOMException.
|
||||||
if (namespace_uri() != Namespace::HTML)
|
if (namespace_uri() != Namespace::HTML)
|
||||||
return WebIDL::NotSupportedError::create(realm(), "Element's namespace is not the HTML namespace"_fly_string);
|
return WebIDL::NotSupportedError::create(realm(), "Element's namespace is not the HTML namespace"_fly_string);
|
||||||
|
|
||||||
// 2. If this’s local name is not one of the following:
|
// 2. If element’s local name is not a valid shadow host name, then throw a "NotSupportedError" DOMException.
|
||||||
// - a valid custom element name
|
if (!is_valid_shadow_host_name(local_name()))
|
||||||
// - "article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p", "section", or "span"
|
return WebIDL::NotSupportedError::create(realm(), "Element's local name is not a valid shadow host name"_fly_string);
|
||||||
if (!HTML::is_valid_custom_element_name(local_name())
|
|
||||||
&& !local_name().is_one_of("article", "aside", "blockquote", "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "header", "main", "nav", "p", "section", "span")) {
|
|
||||||
// then throw a "NotSupportedError" DOMException.
|
|
||||||
return WebIDL::NotSupportedError::create(realm(), MUST(String::formatted("Element '{}' cannot be a shadow host", local_name())));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. If this’s local name is a valid custom element name, or this’s is value is not null, then:
|
// 3. If element’s local name is a valid custom element name, or element’s is value is not null, then:
|
||||||
if (HTML::is_valid_custom_element_name(local_name()) || m_is_value.has_value()) {
|
if (HTML::is_valid_custom_element_name(local_name()) || m_is_value.has_value()) {
|
||||||
// 1. Let definition be the result of looking up a custom element definition given this’s node document, its namespace, its local name, and its is value.
|
// 1. Let definition be the result of looking up a custom element definition given element’s node document, its namespace, its local name, and its is value.
|
||||||
auto definition = document().lookup_custom_element_definition(namespace_uri(), local_name(), m_is_value);
|
auto definition = document().lookup_custom_element_definition(namespace_uri(), local_name(), m_is_value);
|
||||||
|
|
||||||
// 2. If definition is not null and definition’s disable shadow is true, then throw a "NotSupportedError" DOMException.
|
// 2. If definition is not null and definition’s disable shadow is true, then throw a "NotSupportedError" DOMException.
|
||||||
|
@ -619,28 +627,66 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ShadowRoot>> Element::attach_shadow(ShadowR
|
||||||
return WebIDL::NotSupportedError::create(realm(), "Cannot attach a shadow root to a custom element that has disabled shadow roots"_fly_string);
|
return WebIDL::NotSupportedError::create(realm(), "Cannot attach a shadow root to a custom element that has disabled shadow roots"_fly_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. If this is a shadow host, then throw an "NotSupportedError" DOMException.
|
// 4. If element is a shadow host, then:
|
||||||
if (is_shadow_host())
|
if (is_shadow_host()) {
|
||||||
|
// 1. Let currentShadowRoot be element’s shadow root.
|
||||||
|
auto current_shadow_root = shadow_root();
|
||||||
|
|
||||||
|
// 2. If any of the following are true:
|
||||||
|
// - currentShadowRoot’s declarative is false; or
|
||||||
|
// - currentShadowRoot’s mode is not mode,
|
||||||
|
// then throw a "NotSupportedError" DOMException.
|
||||||
|
if (!current_shadow_root->declarative() || current_shadow_root->mode() != mode) {
|
||||||
return WebIDL::NotSupportedError::create(realm(), "Element already is a shadow host"_fly_string);
|
return WebIDL::NotSupportedError::create(realm(), "Element already is a shadow host"_fly_string);
|
||||||
|
}
|
||||||
|
|
||||||
// 5. Let shadow be a new shadow root whose node document is this’s node document, host is this, and mode is init["mode"].
|
// 3. Otherwise:
|
||||||
auto shadow = heap().allocate<ShadowRoot>(realm(), document(), *this, init.mode);
|
// 1. Remove all of currentShadowRoot’s children, in tree order.
|
||||||
|
current_shadow_root->remove_all_children();
|
||||||
|
|
||||||
// 6. Set shadow’s delegates focus to init["delegatesFocus"].
|
// 2. Set currentShadowRoot’s declarative to false.
|
||||||
shadow->set_delegates_focus(init.delegates_focus);
|
current_shadow_root->set_declarative(false);
|
||||||
|
|
||||||
// 7. If this’s custom element state is "precustomized" or "custom", then set shadow’s available to element internals to true.
|
// 3. Return.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Let shadow be a new shadow root whose node document is element’s node document, host is this, and mode is mode.
|
||||||
|
auto shadow = heap().allocate<ShadowRoot>(realm(), document(), *this, mode);
|
||||||
|
|
||||||
|
// 6. Set shadow’s delegates focus to delegatesFocus".
|
||||||
|
shadow->set_delegates_focus(delegates_focus);
|
||||||
|
|
||||||
|
// 7. If element’s custom element state is "precustomized" or "custom", then set shadow’s available to element internals to true.
|
||||||
if (m_custom_element_state == CustomElementState::Precustomized || m_custom_element_state == CustomElementState::Custom)
|
if (m_custom_element_state == CustomElementState::Precustomized || m_custom_element_state == CustomElementState::Custom)
|
||||||
shadow->set_available_to_element_internals(true);
|
shadow->set_available_to_element_internals(true);
|
||||||
|
|
||||||
// 8. Set shadow’s slot assignment to init["slotAssignment"].
|
// 8. Set shadow’s slot assignment to slotAssignment.
|
||||||
shadow->set_slot_assignment(init.slot_assignment);
|
shadow->set_slot_assignment(slot_assignment);
|
||||||
|
|
||||||
// 9. Set this’s shadow root to shadow.
|
// 9. Set shadow’s declarative to false.
|
||||||
|
shadow->set_declarative(false);
|
||||||
|
|
||||||
|
// 10. Set shadow’s clonable to clonable.
|
||||||
|
shadow->set_clonable(clonable);
|
||||||
|
|
||||||
|
// 11. Set shadow’s serializable to serializable.
|
||||||
|
shadow->set_serializable(serializable);
|
||||||
|
|
||||||
|
// 12. Set element’s shadow root to shadow.
|
||||||
set_shadow_root(shadow);
|
set_shadow_root(shadow);
|
||||||
|
|
||||||
// 10. Return shadow.
|
return {};
|
||||||
return shadow;
|
}
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#dom-element-attachshadow
|
||||||
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<ShadowRoot>> Element::attach_shadow(ShadowRootInit init)
|
||||||
|
{
|
||||||
|
// 1. Run attach a shadow root with this, init["mode"], init["clonable"], init["serializable"], init["delegatesFocus"], and init["slotAssignment"].
|
||||||
|
TRY(attach_a_shadow_root(init.mode, init.clonable, init.serializable, init.delegates_focus, init.slot_assignment));
|
||||||
|
|
||||||
|
// 2. Return this’s shadow root.
|
||||||
|
return JS::NonnullGCPtr { *shadow_root() };
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-element-shadowroot
|
// https://dom.spec.whatwg.org/#dom-element-shadowroot
|
||||||
|
|
|
@ -33,6 +33,8 @@ struct ShadowRootInit {
|
||||||
Bindings::ShadowRootMode mode;
|
Bindings::ShadowRootMode mode;
|
||||||
bool delegates_focus = false;
|
bool delegates_focus = false;
|
||||||
Bindings::SlotAssignmentMode slot_assignment { Bindings::SlotAssignmentMode::Named };
|
Bindings::SlotAssignmentMode slot_assignment { Bindings::SlotAssignmentMode::Named };
|
||||||
|
bool clonable = false;
|
||||||
|
bool serializable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://w3c.github.io/csswg-drafts/cssom-view-1/#dictdef-scrollintoviewoptions
|
// https://w3c.github.io/csswg-drafts/cssom-view-1/#dictdef-scrollintoviewoptions
|
||||||
|
@ -126,6 +128,7 @@ public:
|
||||||
DOMTokenList* class_list();
|
DOMTokenList* class_list();
|
||||||
|
|
||||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<ShadowRoot>> attach_shadow(ShadowRootInit init);
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<ShadowRoot>> attach_shadow(ShadowRootInit init);
|
||||||
|
WebIDL::ExceptionOr<void> attach_a_shadow_root(Bindings::ShadowRootMode mode, bool clonable, bool serializable, bool delegates_focus, Bindings::SlotAssignmentMode slot_assignment);
|
||||||
JS::GCPtr<ShadowRoot> shadow_root() const;
|
JS::GCPtr<ShadowRoot> shadow_root() const;
|
||||||
|
|
||||||
WebIDL::ExceptionOr<bool> matches(StringView selectors) const;
|
WebIDL::ExceptionOr<bool> matches(StringView selectors) const;
|
||||||
|
|
|
@ -104,6 +104,8 @@ dictionary ShadowRootInit {
|
||||||
required ShadowRootMode mode;
|
required ShadowRootMode mode;
|
||||||
boolean delegatesFocus = false;
|
boolean delegatesFocus = false;
|
||||||
SlotAssignmentMode slotAssignment = "named";
|
SlotAssignmentMode slotAssignment = "named";
|
||||||
|
boolean clonable = false;
|
||||||
|
boolean serializable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
Element includes ParentNode;
|
Element includes ParentNode;
|
||||||
|
|
|
@ -25,6 +25,15 @@ public:
|
||||||
bool delegates_focus() const { return m_delegates_focus; }
|
bool delegates_focus() const { return m_delegates_focus; }
|
||||||
void set_delegates_focus(bool delegates_focus) { m_delegates_focus = delegates_focus; }
|
void set_delegates_focus(bool delegates_focus) { m_delegates_focus = delegates_focus; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool declarative() const { return m_declarative; }
|
||||||
|
void set_declarative(bool declarative) { m_declarative = declarative; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool clonable() const { return m_clonable; }
|
||||||
|
void set_clonable(bool clonable) { m_clonable = clonable; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool serializable() const { return m_serializable; }
|
||||||
|
void set_serializable(bool serializable) { m_serializable = serializable; }
|
||||||
|
|
||||||
void set_onslotchange(WebIDL::CallbackType*);
|
void set_onslotchange(WebIDL::CallbackType*);
|
||||||
WebIDL::CallbackType* onslotchange();
|
WebIDL::CallbackType* onslotchange();
|
||||||
|
|
||||||
|
@ -68,6 +77,15 @@ private:
|
||||||
bool m_delegates_focus { false };
|
bool m_delegates_focus { false };
|
||||||
bool m_available_to_element_internals { false };
|
bool m_available_to_element_internals { false };
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#shadowroot-declarative
|
||||||
|
bool m_declarative { false };
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#shadowroot-clonable
|
||||||
|
bool m_clonable { false };
|
||||||
|
|
||||||
|
// https://dom.spec.whatwg.org/#shadowroot-serializable
|
||||||
|
bool m_serializable { false };
|
||||||
|
|
||||||
JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
|
JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
|
||||||
mutable JS::GCPtr<WebIDL::ObservableArray> m_adopted_style_sheets;
|
mutable JS::GCPtr<WebIDL::ObservableArray> m_adopted_style_sheets;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue