Files
servo/components/script/dom/html/htmloptgroupelement.rs
Tim van der Lippe a1c8896eda script: Pass &mut JSContext to reflect_node_with_proto (#43952)
A lot (and I mean, really a lot) depends on these constructors.
Therefore, this is the one spaghetti ball that I could extract and
convert all `can_gc` to `cx`. There are some new introductions of
`temp_cx` in the callbacks of the servo parser, but we already had some
in other callbacks.

Part of #40600

Testing: It compiles

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
2026-04-05 18:07:30 +00:00

160 lines
5.4 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix, local_name};
use js::context::JSContext;
use js::rust::HandleObject;
use script_bindings::str::DOMString;
use stylo_dom::ElementState;
use crate::dom::attr::Attr;
use crate::dom::bindings::codegen::Bindings::HTMLOptGroupElementBinding::HTMLOptGroupElementMethods;
use crate::dom::bindings::codegen::GenericBindings::NodeBinding::Node_Binding::NodeMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::html::htmlelement::HTMLElement;
use crate::dom::html::htmloptionelement::HTMLOptionElement;
use crate::dom::html::htmlselectelement::HTMLSelectElement;
use crate::dom::node::{BindContext, Node, UnbindContext};
use crate::dom::validation::Validatable;
use crate::dom::validitystate::ValidationFlags;
use crate::dom::virtualmethods::VirtualMethods;
use crate::script_runtime::CanGc;
/// <https://html.spec.whatwg.org/multipage/#htmloptgroupelement>
#[dom_struct]
pub(crate) struct HTMLOptGroupElement {
htmlelement: HTMLElement,
}
impl HTMLOptGroupElement {
fn new_inherited(
local_name: LocalName,
prefix: Option<Prefix>,
document: &Document,
) -> HTMLOptGroupElement {
HTMLOptGroupElement {
htmlelement: HTMLElement::new_inherited_with_state(
ElementState::ENABLED,
local_name,
prefix,
document,
),
}
}
pub(crate) fn new(
cx: &mut js::context::JSContext,
local_name: LocalName,
prefix: Option<Prefix>,
document: &Document,
proto: Option<HandleObject>,
) -> DomRoot<HTMLOptGroupElement> {
Node::reflect_node_with_proto(
cx,
Box::new(HTMLOptGroupElement::new_inherited(
local_name, prefix, document,
)),
document,
proto,
)
}
fn update_select_validity(&self, can_gc: CanGc) {
if let Some(select) = self.owner_select_element() {
select
.validity_state(can_gc)
.perform_validation_and_update(ValidationFlags::all(), can_gc);
}
}
fn owner_select_element(&self) -> Option<DomRoot<HTMLSelectElement>> {
self.upcast::<Node>()
.GetParentNode()
.and_then(DomRoot::downcast)
}
}
impl HTMLOptGroupElementMethods<crate::DomTypeHolder> for HTMLOptGroupElement {
// https://html.spec.whatwg.org/multipage/#dom-optgroup-disabled
make_bool_getter!(Disabled, "disabled");
// https://html.spec.whatwg.org/multipage/#dom-optgroup-disabled
make_bool_setter!(SetDisabled, "disabled");
// https://html.spec.whatwg.org/multipage/#dom-optgroup-label
make_getter!(Label, "label");
// https://html.spec.whatwg.org/multipage/#dom-optgroup-label
make_setter!(SetLabel, "label");
}
impl VirtualMethods for HTMLOptGroupElement {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn attribute_mutated(
&self,
cx: &mut js::context::JSContext,
attr: &Attr,
mutation: AttributeMutation,
) {
self.super_type()
.unwrap()
.attribute_mutated(cx, attr, mutation);
if attr.local_name() == &local_name!("disabled") {
let disabled_state = match mutation {
AttributeMutation::Set(None, _) => true,
AttributeMutation::Set(Some(_), _) => {
// Option group was already disabled.
return;
},
AttributeMutation::Removed => false,
};
let el = self.upcast::<Element>();
el.set_disabled_state(disabled_state);
el.set_enabled_state(!disabled_state);
let options = el
.upcast::<Node>()
.children()
.filter(|child| child.is::<HTMLOptionElement>())
.map(|child| DomRoot::from_ref(child.downcast::<HTMLOptionElement>().unwrap()));
if disabled_state {
for option in options {
let el = option.upcast::<Element>();
el.set_disabled_state(true);
el.set_enabled_state(false);
}
} else {
for option in options {
let el = option.upcast::<Element>();
el.check_disabled_attribute();
}
}
}
}
fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
if let Some(super_type) = self.super_type() {
super_type.bind_to_tree(cx, context);
}
self.update_select_validity(CanGc::from_cx(cx));
}
fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
self.super_type().unwrap().unbind_from_tree(context, can_gc);
if let Some(select) = context.parent.downcast::<HTMLSelectElement>() {
select
.validity_state(can_gc)
.perform_validation_and_update(ValidationFlags::all(), can_gc);
}
}
}