diff options
Diffstat (limited to 'components/script/dom/htmloptionelement.rs')
-rw-r--r-- | components/script/dom/htmloptionelement.rs | 201 |
1 files changed, 147 insertions, 54 deletions
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index e54b9b563e4..0ba7eadcf11 100644 --- a/components/script/dom/htmloptionelement.rs +++ b/components/script/dom/htmloptionelement.rs @@ -1,64 +1,109 @@ /* 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 http://mozilla.org/MPL/2.0/. */ - -use dom::attr::Attr; -use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; -use dom::bindings::codegen::Bindings::HTMLOptionElementBinding; -use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; -use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementBinding::HTMLSelectElementMethods; -use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::Root; -use dom::bindings::str::DOMString; -use dom::characterdata::CharacterData; -use dom::document::Document; -use dom::element::{AttributeMutation, Element}; -use dom::htmlelement::HTMLElement; -use dom::htmlformelement::HTMLFormElement; -use dom::htmloptgroupelement::HTMLOptGroupElement; -use dom::htmlscriptelement::HTMLScriptElement; -use dom::htmlselectelement::HTMLSelectElement; -use dom::node::{Node, UnbindContext}; -use dom::text::Text; -use dom::virtualmethods::VirtualMethods; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::attr::Attr; +use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; +use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; +use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementBinding::HTMLSelectElementMethods; +use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use crate::dom::bindings::error::Fallible; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::characterdata::CharacterData; +use crate::dom::document::Document; +use crate::dom::element::{AttributeMutation, CustomElementCreationMode, Element, ElementCreator}; +use crate::dom::htmlelement::HTMLElement; +use crate::dom::htmlformelement::HTMLFormElement; +use crate::dom::htmloptgroupelement::HTMLOptGroupElement; +use crate::dom::htmlscriptelement::HTMLScriptElement; +use crate::dom::htmlselectelement::HTMLSelectElement; +use crate::dom::node::{BindContext, Node, ShadowIncluding, UnbindContext}; +use crate::dom::text::Text; +use crate::dom::virtualmethods::VirtualMethods; +use crate::dom::window::Window; use dom_struct::dom_struct; -use html5ever_atoms::LocalName; +use html5ever::{LocalName, Prefix, QualName}; use std::cell::Cell; -use style::element_state::*; +use std::convert::TryInto; +use style::element_state::ElementState; use style::str::{split_html_space_chars, str_join}; #[dom_struct] pub struct HTMLOptionElement { htmlelement: HTMLElement, - /// https://html.spec.whatwg.org/multipage/#attr-option-selected + /// <https://html.spec.whatwg.org/multipage/#attr-option-selected> selectedness: Cell<bool>, - /// https://html.spec.whatwg.org/multipage/#concept-option-dirtiness + /// <https://html.spec.whatwg.org/multipage/#concept-option-dirtiness> dirtiness: Cell<bool>, } impl HTMLOptionElement { - fn new_inherited(local_name: LocalName, - prefix: Option<DOMString>, - document: &Document) -> HTMLOptionElement { + fn new_inherited( + local_name: LocalName, + prefix: Option<Prefix>, + document: &Document, + ) -> HTMLOptionElement { HTMLOptionElement { - htmlelement: - HTMLElement::new_inherited_with_state(IN_ENABLED_STATE, - local_name, prefix, document), + htmlelement: HTMLElement::new_inherited_with_state( + ElementState::IN_ENABLED_STATE, + local_name, + prefix, + document, + ), selectedness: Cell::new(false), dirtiness: Cell::new(false), } } #[allow(unrooted_must_root)] - pub fn new(local_name: LocalName, - prefix: Option<DOMString>, - document: &Document) -> Root<HTMLOptionElement> { - Node::reflect_node(box HTMLOptionElement::new_inherited(local_name, prefix, document), - document, - HTMLOptionElementBinding::Wrap) + pub fn new( + local_name: LocalName, + prefix: Option<Prefix>, + document: &Document, + ) -> DomRoot<HTMLOptionElement> { + Node::reflect_node( + Box::new(HTMLOptionElement::new_inherited( + local_name, prefix, document, + )), + document, + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-option + #[allow(non_snake_case)] + pub fn Option( + window: &Window, + text: DOMString, + value: Option<DOMString>, + default_selected: bool, + selected: bool, + ) -> Fallible<DomRoot<HTMLOptionElement>> { + let element = Element::create( + QualName::new(None, ns!(html), local_name!("option")), + None, + &window.Document(), + ElementCreator::ScriptCreated, + CustomElementCreationMode::Synchronous, + ); + + let option = DomRoot::downcast::<HTMLOptionElement>(element).unwrap(); + + if !text.is_empty() { + option.upcast::<Node>().SetTextContent(Some(text)) + } + + if let Some(val) = value { + option.SetValue(val) + } + + option.SetDefaultSelected(default_selected); + option.set_selectedness(selected); + Ok(option) } pub fn set_selectedness(&self, selected: bool) { @@ -70,20 +115,59 @@ impl HTMLOptionElement { } fn pick_if_selected_and_reset(&self) { - if let Some(select) = self.upcast::<Node>().ancestors() - .filter_map(Root::downcast::<HTMLSelectElement>) - .next() { + if let Some(select) = self + .upcast::<Node>() + .ancestors() + .filter_map(DomRoot::downcast::<HTMLSelectElement>) + .next() + { if self.Selected() { select.pick_option(self); } select.ask_for_reset(); } } + + // https://html.spec.whatwg.org/multipage/#concept-option-index + fn index(&self) -> i32 { + if let Some(parent) = self.upcast::<Node>().GetParentNode() { + if let Some(select_parent) = parent.downcast::<HTMLSelectElement>() { + // return index in parent select's list of options + return self.index_in_select(select_parent); + } else if parent.is::<HTMLOptGroupElement>() { + if let Some(grandparent) = parent.GetParentNode() { + if let Some(select_grandparent) = grandparent.downcast::<HTMLSelectElement>() { + // return index in grandparent select's list of options + return self.index_in_select(select_grandparent); + } + } + } + } + // "If the option element is not in a list of options, + // then the option element's index is zero." + // self is neither a child of a select, nor a grandchild of a select + // via an optgroup, so it is not in a list of options + 0 + } + + fn index_in_select(&self, select: &HTMLSelectElement) -> i32 { + match select.list_of_options().position(|n| &*n == self) { + Some(index) => index.try_into().unwrap_or(0), + None => { + // shouldn't happen but not worth a browser panic + warn!( + "HTMLOptionElement called index_in_select at a select that did not contain it" + ); + 0 + }, + } + } } // FIXME(ajeffrey): Provide a way of buffering DOMStrings other than using Strings fn collect_text(element: &Element, value: &mut String) { - let svg_script = *element.namespace() == ns!(svg) && element.local_name() == &local_name!("script"); + let svg_script = + *element.namespace() == ns!(svg) && element.local_name() == &local_name!("script"); let html_script = element.is::<HTMLScriptElement>(); if svg_script || html_script { return; @@ -119,14 +203,14 @@ impl HTMLOptionElementMethods for HTMLOptionElement { } // https://html.spec.whatwg.org/multipage/#dom-option-form - fn GetForm(&self) -> Option<Root<HTMLFormElement>> { - let parent = self.upcast::<Node>().GetParentNode().and_then(|p| + fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> { + let parent = self.upcast::<Node>().GetParentNode().and_then(|p| { if p.is::<HTMLOptGroupElement>() { p.upcast::<Node>().GetParentNode() } else { Some(p) } - ); + }); parent.and_then(|p| p.downcast::<HTMLSelectElement>().and_then(|s| s.GetForm())) } @@ -176,11 +260,16 @@ impl HTMLOptionElementMethods for HTMLOptionElement { self.selectedness.set(selected); self.pick_if_selected_and_reset(); } + + // https://html.spec.whatwg.org/multipage/#dom-option-index + fn Index(&self) -> i32 { + self.index() + } } impl VirtualMethods for HTMLOptionElement { - fn super_type(&self) -> Option<&VirtualMethods> { - Some(self.upcast::<HTMLElement>() as &VirtualMethods) + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods) } fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) { @@ -197,7 +286,7 @@ impl VirtualMethods for HTMLOptionElement { el.set_disabled_state(false); el.set_enabled_state(true); el.check_parent_disabled_state_for_option(); - } + }, } }, &local_name!("selected") => { @@ -220,12 +309,13 @@ impl VirtualMethods for HTMLOptionElement { } } - fn bind_to_tree(&self, tree_in_doc: bool) { + fn bind_to_tree(&self, context: &BindContext) { if let Some(ref s) = self.super_type() { - s.bind_to_tree(tree_in_doc); + s.bind_to_tree(context); } - self.upcast::<Element>().check_parent_disabled_state_for_option(); + self.upcast::<Element>() + .check_parent_disabled_state_for_option(); self.pick_if_selected_and_reset(); } @@ -233,9 +323,12 @@ impl VirtualMethods for HTMLOptionElement { fn unbind_from_tree(&self, context: &UnbindContext) { self.super_type().unwrap().unbind_from_tree(context); - if let Some(select) = context.parent.inclusive_ancestors() - .filter_map(Root::downcast::<HTMLSelectElement>) - .next() { + if let Some(select) = context + .parent + .inclusive_ancestors(ShadowIncluding::No) + .filter_map(DomRoot::downcast::<HTMLSelectElement>) + .next() + { select.ask_for_reset(); } |