Implements :indeterminate pseudo-class

Addresses reviews
This commit is contained in:
Matthew Rasmus 2014-12-08 11:29:42 -08:00
parent 2d05ac537c
commit 504f968b20
14 changed files with 105 additions and 206 deletions

View file

@ -562,6 +562,13 @@ impl<'le> TElement<'le> for LayoutElement<'le> {
} }
} }
#[inline]
fn get_indeterminate_state(self) -> bool {
unsafe {
self.element.get_indeterminate_state_for_layout()
}
}
#[inline] #[inline]
fn has_class(self, name: &Atom) -> bool { fn has_class(self, name: &Atom) -> bool {
unsafe { unsafe {

View file

@ -35,7 +35,7 @@ use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers}; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementHelpers}; use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementHelpers};
use dom::htmlcollection::HTMLCollection; use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers}; use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers, HTMLInputElementHelpers};
use dom::htmlserializer::serialize; use dom::htmlserializer::serialize;
use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers}; use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers};
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers}; use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
@ -211,6 +211,7 @@ pub trait RawLayoutElementHelpers {
unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute) unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute)
-> Option<i32>; -> Option<i32>;
unsafe fn get_checked_state_for_layout(&self) -> bool; unsafe fn get_checked_state_for_layout(&self) -> bool;
unsafe fn get_indeterminate_state_for_layout(&self) -> bool;
unsafe fn get_unsigned_integer_attribute_for_layout(&self, attribute: UnsignedIntegerAttribute) unsafe fn get_unsigned_integer_attribute_for_layout(&self, attribute: UnsignedIntegerAttribute)
-> Option<u32>; -> Option<u32>;
unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute) unsafe fn get_simple_color_attribute_for_layout(&self, attribute: SimpleColorAttribute)
@ -337,6 +338,18 @@ impl RawLayoutElementHelpers for Element {
this.get_checked_state_for_layout() this.get_checked_state_for_layout()
} }
#[inline]
#[allow(unrooted_must_root)]
unsafe fn get_indeterminate_state_for_layout(&self) -> bool {
// TODO progress elements can also be matched with :indeterminate
if !self.is_htmlinputelement() {
return false
}
let this: &HTMLInputElement = mem::transmute(self);
this.get_indeterminate_state_for_layout()
}
unsafe fn get_unsigned_integer_attribute_for_layout(&self, unsafe fn get_unsigned_integer_attribute_for_layout(&self,
attribute: UnsignedIntegerAttribute) attribute: UnsignedIntegerAttribute)
-> Option<u32> { -> Option<u32> {
@ -1274,6 +1287,12 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
None => false, None => false,
} }
} }
fn get_indeterminate_state(self) -> bool {
match HTMLInputElementCast::to_ref(self) {
Some(input) => input.get_indeterminate_state(),
None => false,
}
}
fn has_class(self, name: &Atom) -> bool { fn has_class(self, name: &Atom) -> bool {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way // FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods. // of disambiguating methods.

View file

@ -2,15 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::HTMLFormElementBinding; use dom::bindings::codegen::Bindings::HTMLFormElementBinding;
use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLFormElementDerived, NodeCast}; use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLFormElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, HTMLTextAreaElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, HTMLTextAreaElementCast, HTMLFormElementCast};
use dom::bindings::global::Window; use dom::bindings::global::Window;
use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::{Document, DocumentHelpers}; use dom::document::{Document, DocumentHelpers};
use dom::element::{Element, AttributeHandlers, HTMLFormElementTypeId, HTMLTextAreaElementTypeId, HTMLDataListElementTypeId}; use dom::element::{Element, AttributeHandlers, HTMLFormElementTypeId, HTMLTextAreaElementTypeId, HTMLDataListElementTypeId};
@ -31,9 +32,12 @@ use url::UrlParser;
use url::form_urlencoded::serialize; use url::form_urlencoded::serialize;
use string_cache::Atom; use string_cache::Atom;
use std::cell::Cell;
#[dom_struct] #[dom_struct]
pub struct HTMLFormElement { pub struct HTMLFormElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
marked_for_reset: Cell<bool>,
} }
impl HTMLFormElementDerived for EventTarget { impl HTMLFormElementDerived for EventTarget {
@ -45,7 +49,8 @@ impl HTMLFormElementDerived for EventTarget {
impl HTMLFormElement { impl HTMLFormElement {
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLFormElement { fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLFormElement {
HTMLFormElement { HTMLFormElement {
htmlelement: HTMLElement::new_inherited(HTMLFormElementTypeId, localName, prefix, document) htmlelement: HTMLElement::new_inherited(HTMLFormElementTypeId, localName, prefix, document),
marked_for_reset: Cell::new(false),
} }
} }
@ -332,6 +337,13 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
} }
fn reset(self, _reset_method_flag: ResetFrom) { fn reset(self, _reset_method_flag: ResetFrom) {
// https://html.spec.whatwg.org/multipage/forms.html#locked-for-reset
if self.marked_for_reset.get() {
return;
} else {
self.marked_for_reset.set(true);
}
let win = window_from_node(self).root(); let win = window_from_node(self).root();
let event = Event::new(Window(*win), let event = Event::new(Window(*win),
"reset".to_string(), "reset".to_string(),
@ -374,6 +386,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
_ => {} _ => {}
} }
}; };
self.marked_for_reset.set(false);
} }
} }
@ -471,7 +484,30 @@ impl<'a> FormSubmitter<'a> {
} }
pub trait FormControl<'a> : Copy { pub trait FormControl<'a> : Copy {
fn form_owner(self) -> Option<Temporary<HTMLFormElement>>; // FIXME: This is wrong (https://github.com/servo/servo/issues/3553)
// but we need html5ever to do it correctly
fn form_owner(self) -> Option<Temporary<HTMLFormElement>> {
// https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner
let elem = self.to_element();
let owner = elem.get_string_attribute(&atom!("form"));
if !owner.is_empty() {
let doc = document_from_node(elem).root();
let owner = doc.GetElementById(owner).root();
match owner {
Some(o) => {
let maybe_form: Option<JSRef<HTMLFormElement>> = HTMLFormElementCast::to_ref(*o);
if maybe_form.is_some() {
return maybe_form.map(Temporary::from_rooted);
}
},
_ => ()
}
}
let node: JSRef<Node> = NodeCast::from_ref(elem);
node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next()
.map(Temporary::from_rooted)
}
fn get_form_attribute(self, fn get_form_attribute(self,
attr: &Atom, attr: &Atom,
input: |Self| -> DOMString, input: |Self| -> DOMString,

View file

@ -7,12 +7,11 @@ use dom::attr::{Attr, AttrValue, UIntAttrValue};
use dom::attr::AttrHelpers; use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding; use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLFormElementCast, HTMLInputElementCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, HTMLInputElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived, EventTargetCast}; use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived, EventTargetCast};
use dom::bindings::codegen::InheritTypes::KeyboardEventCast; use dom::bindings::codegen::InheritTypes::KeyboardEventCast;
use dom::bindings::global::Window; use dom::bindings::global::Window;
@ -134,6 +133,7 @@ pub trait LayoutHTMLInputElementHelpers {
pub trait RawLayoutHTMLInputElementHelpers { pub trait RawLayoutHTMLInputElementHelpers {
unsafe fn get_checked_state_for_layout(&self) -> bool; unsafe fn get_checked_state_for_layout(&self) -> bool;
unsafe fn get_indeterminate_state_for_layout(&self) -> bool;
unsafe fn get_size_for_layout(&self) -> u32; unsafe fn get_size_for_layout(&self) -> u32;
} }
@ -176,6 +176,11 @@ impl RawLayoutHTMLInputElementHelpers for HTMLInputElement {
self.checked.get() self.checked.get()
} }
#[allow(unrooted_must_root)]
unsafe fn get_indeterminate_state_for_layout(&self) -> bool {
self.indeterminate.get()
}
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn get_size_for_layout(&self) -> u32 { unsafe fn get_size_for_layout(&self) -> u32 {
self.size.get() self.size.get()
@ -284,7 +289,6 @@ impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// https://html.spec.whatwg.org/multipage/forms.html#dom-input-indeterminate // https://html.spec.whatwg.org/multipage/forms.html#dom-input-indeterminate
fn SetIndeterminate(self, val: bool) { fn SetIndeterminate(self, val: bool) {
// FIXME #4079 this should change the appearance
self.indeterminate.set(val) self.indeterminate.set(val)
} }
} }
@ -295,6 +299,7 @@ pub trait HTMLInputElementHelpers {
fn get_radio_group_name(self) -> Option<String>; fn get_radio_group_name(self) -> Option<String>;
fn update_checked_state(self, checked: bool, dirty: bool); fn update_checked_state(self, checked: bool, dirty: bool);
fn get_size(&self) -> u32; fn get_size(&self) -> u32;
fn get_indeterminate_state(self) -> bool;
} }
fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) { fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) {
@ -373,6 +378,10 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
fn get_size(&self) -> u32 { fn get_size(&self) -> u32 {
self.size.get() self.size.get()
} }
fn get_indeterminate_state(self) -> bool {
self.indeterminate.get()
}
} }
impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
@ -566,30 +575,6 @@ impl Reflectable for HTMLInputElement {
} }
impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> { impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> {
// FIXME: This is wrong (https://github.com/servo/servo/issues/3553)
// but we need html5ever to do it correctly
fn form_owner(self) -> Option<Temporary<HTMLFormElement>> {
// https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner
let elem: JSRef<Element> = ElementCast::from_ref(self);
let owner = elem.get_string_attribute(&atom!("form"));
if !owner.is_empty() {
let doc = document_from_node(self).root();
let owner = doc.GetElementById(owner).root();
match owner {
Some(o) => {
let maybe_form: Option<JSRef<HTMLFormElement>> = HTMLFormElementCast::to_ref(*o);
if maybe_form.is_some() {
return maybe_form.map(Temporary::from_rooted);
}
},
_ => ()
}
}
let node: JSRef<Node> = NodeCast::from_ref(self);
node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next()
.map(Temporary::from_rooted)
}
fn to_element(self) -> JSRef<'a, Element> { fn to_element(self) -> JSRef<'a, Element> {
ElementCast::from_ref(self) ElementCast::from_ref(self)
} }
@ -601,6 +586,7 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> {
!(self.Disabled() || self.ReadOnly()) !(self.Disabled() || self.ReadOnly())
} }
// https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-form-reset-control
fn reset(self) { fn reset(self) {
match self.input_type.get() { match self.input_type.get() {
InputRadio | InputCheckbox => { InputRadio | InputCheckbox => {
@ -632,6 +618,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
match ty { match ty {
// https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior // https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior
// InputSubmit => (), // No behavior defined // InputSubmit => (), // No behavior defined
// https://html.spec.whatwg.org/multipage/forms.html#reset-button-state-(type=reset):activation-behavior
// InputSubmit => (), // No behavior defined
InputCheckbox => { InputCheckbox => {
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):pre-click-activation-steps // https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):pre-click-activation-steps
// cache current values of `checked` and `indeterminate` // cache current values of `checked` and `indeterminate`
@ -679,6 +667,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
match ty { match ty {
// https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior // https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior
// InputSubmit => (), // No behavior defined // InputSubmit => (), // No behavior defined
// https://html.spec.whatwg.org/multipage/forms.html#reset-button-state-(type=reset):activation-behavior
// InputReset => (), // No behavior defined
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):canceled-activation-steps // https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):canceled-activation-steps
InputCheckbox => { InputCheckbox => {
// We want to restore state only if the element had been changed in the first place // We want to restore state only if the element had been changed in the first place

View file

@ -5,14 +5,13 @@
use dom::attr::{Attr, AttrValue}; use dom::attr::{Attr, AttrValue};
use dom::attr::AttrHelpers; use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell; use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding; use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods; use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast}; use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived}; use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::InheritTypes::{KeyboardEventCast, TextDerived, HTMLFormElementCast}; use dom::bindings::codegen::InheritTypes::{KeyboardEventCast, TextDerived};
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable}; use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::{Document, DocumentHelpers}; use dom::document::{Document, DocumentHelpers};
@ -20,7 +19,7 @@ use dom::element::{AttributeHandlers, HTMLTextAreaElementTypeId, Element};
use dom::event::Event; use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement; use dom::htmlelement::HTMLElement;
use dom::htmlformelement::{FormControl, HTMLFormElement}; use dom::htmlformelement::FormControl;
use dom::keyboardevent::KeyboardEvent; use dom::keyboardevent::KeyboardEvent;
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, OtherNodeDamage, ElementNodeTypeId}; use dom::node::{DisabledStateHelpers, Node, NodeHelpers, OtherNodeDamage, ElementNodeTypeId};
use dom::node::{document_from_node}; use dom::node::{document_from_node};
@ -299,30 +298,6 @@ impl Reflectable for HTMLTextAreaElement {
} }
impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> { impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> {
// FIXME: This is wrong (https://github.com/servo/servo/issues/3553)
// but we need html5ever to do it correctly
fn form_owner(self) -> Option<Temporary<HTMLFormElement>> {
// https://html.spec.whatwg.org/multipage/forms.html#reset-the-form-owner
let elem: JSRef<Element> = ElementCast::from_ref(self);
let owner = elem.get_string_attribute(&atom!("form"));
if !owner.is_empty() {
let doc = document_from_node(self).root();
let owner = doc.GetElementById(owner).root();
match owner {
Some(o) => {
let maybe_form: Option<JSRef<HTMLFormElement>> = HTMLFormElementCast::to_ref(*o);
if maybe_form.is_some() {
return maybe_form.map(Temporary::from_rooted);
}
},
_ => ()
}
}
let node: JSRef<Node> = NodeCast::from_ref(self);
node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next()
.map(Temporary::from_rooted)
}
fn to_element(self) -> JSRef<'a, Element> { fn to_element(self) -> JSRef<'a, Element> {
ElementCast::from_ref(self) ElementCast::from_ref(self)
} }
@ -334,6 +309,7 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> {
} }
fn reset(self) { fn reset(self) {
// https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-form-reset-control
self.SetValue(self.DefaultValue()); self.SetValue(self.DefaultValue());
self.value_changed.set(false); self.value_changed.set(false);
} }

View file

@ -47,6 +47,7 @@ pub trait TElement<'a> : Copy {
fn get_disabled_state(self) -> bool; fn get_disabled_state(self) -> bool;
fn get_enabled_state(self) -> bool; fn get_enabled_state(self) -> bool;
fn get_checked_state(self) -> bool; fn get_checked_state(self) -> bool;
fn get_indeterminate_state(self) -> bool;
fn has_class(self, name: &Atom) -> bool; fn has_class(self, name: &Atom) -> bool;
fn has_nonzero_border(self) -> bool; fn has_nonzero_border(self) -> bool;

View file

@ -23,7 +23,7 @@ use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use selectors::{After, AnyLink, AttrDashMatch, AttrEqual}; use selectors::{After, AnyLink, AttrDashMatch, AttrEqual};
use selectors::{AttrExists, AttrIncludes, AttrPrefixMatch}; use selectors::{AttrExists, AttrIncludes, AttrPrefixMatch};
use selectors::{AttrSubstringMatch, AttrSuffixMatch, Before, CaseInsensitive, CaseSensitive}; use selectors::{AttrSubstringMatch, AttrSuffixMatch, Before, CaseInsensitive, CaseSensitive};
use selectors::{Checked, Child, ClassSelector}; use selectors::{Checked, Child, ClassSelector, Indeterminate};
use selectors::{CompoundSelector, Descendant, Disabled, Enabled, FirstChild, FirstOfType}; use selectors::{CompoundSelector, Descendant, Disabled, Enabled, FirstChild, FirstOfType};
use selectors::{Hover, IDSelector, LastChild, LastOfType}; use selectors::{Hover, IDSelector, LastChild, LastOfType};
use selectors::{LaterSibling, LocalName, LocalNameSelector}; use selectors::{LaterSibling, LocalName, LocalNameSelector};
@ -972,6 +972,12 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
let elem = element.as_element(); let elem = element.as_element();
elem.get_checked_state() elem.get_checked_state()
} }
// https://html.spec.whatwg.org/multipage/scripting.html#selector-indeterminate
Indeterminate => {
*shareable = false;
let elem = element.as_element();
elem.get_indeterminate_state()
}
FirstChild => { FirstChild => {
*shareable = false; *shareable = false;
matches_first_child(element) matches_first_child(element)

View file

@ -75,6 +75,7 @@ pub enum SimpleSelector {
Disabled, Disabled,
Enabled, Enabled,
Checked, Checked,
Indeterminate,
FirstChild, LastChild, OnlyChild, FirstChild, LastChild, OnlyChild,
// Empty, // Empty,
Root, Root,
@ -167,7 +168,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
| &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..) | &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..)
| &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..) | &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..)
| &AnyLink | &Link | &Visited | &Hover | &Disabled | &Enabled | &AnyLink | &Link | &Visited | &Hover | &Disabled | &Enabled
| &FirstChild | &LastChild | &OnlyChild | &Root | &Checked | &FirstChild | &LastChild | &OnlyChild | &Root | &Checked | &Indeterminate
// | &Empty | &Lang(*) // | &Empty | &Lang(*)
| &NthChild(..) | &NthLastChild(..) | &NthChild(..) | &NthLastChild(..)
| &NthOfType(..) | &NthLastOfType(..) | &NthOfType(..) | &NthLastOfType(..)
@ -568,6 +569,7 @@ fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<Simp
"disabled" => Ok(Disabled), "disabled" => Ok(Disabled),
"enabled" => Ok(Enabled), "enabled" => Ok(Enabled),
"checked" => Ok(Checked), "checked" => Ok(Checked),
"indeterminate" => Ok(Indeterminate),
"first-child" => Ok(FirstChild), "first-child" => Ok(FirstChild),
"last-child" => Ok(LastChild), "last-child" => Ok(LastChild),
"only-child" => Ok(OnlyChild), "only-child" => Ok(OnlyChild),

View file

@ -11,6 +11,7 @@ input[type="radio"] { font-family: monospace !important; border: none !impor
input[type="checkbox"]::before { content: "[ ]"; padding: 0; } input[type="checkbox"]::before { content: "[ ]"; padding: 0; }
input[type="checkbox"]:checked::before { content: "[✓]"; } input[type="checkbox"]:checked::before { content: "[✓]"; }
input[type="checkbox"]:indeterminate::before { content: "[-]"; }
input[type="radio"]::before { content: "( )"; padding: 0; } input[type="radio"]::before { content: "( )"; padding: 0; }
input[type="radio"]:checked::before { content: "(●)"; } input[type="radio"]:checked::before { content: "(●)"; }

View file

@ -1,8 +1,7 @@
<html> <html>
<head></head> <head></head>
<body> <body>
<!-- Run with nc -l 8000 --> <form>
<form action="http://localhost:8000" method=get id="foo">
<input name=bar type=checkbox checked> <input name=bar type=checkbox checked>
<input name=baz value="baz1" type=radio checked> <input name=baz value="baz1" type=radio checked>
<input name=baz value="baz2" type=radio> <input name=baz value="baz2" type=radio>

View file

@ -1,8 +1,7 @@
<html> <html>
<head></head> <head></head>
<body> <body>
<!-- Run with nc -l 8000 --> <form id="foo">
<form action="http://localhost:8000" method=get id="foo">
<input name=bar type=checkbox checked> <input name=bar type=checkbox checked>
<input name=baz value="baz1" type=radio checked> <input name=baz value="baz1" type=radio checked>
<input name=baz value="baz2" type=radio> <input name=baz value="baz2" type=radio>

View file

@ -12,7 +12,11 @@
<div><input id=foo3 type="checkbox"></div> <div><input id=foo3 type="checkbox"></div>
<div><button id=setdefault type=button>setDefaultChecked</button></div> <div><button id=setdefault type=button>setDefaultChecked</button></div>
<div><input type="submit"><input type="reset"><div> <div><input type="submit"><input type="reset"><div>
<div><input id=ch type="checkbox" checked></div>
<div><input id=unch type="checkbox"></div>
<script> <script>
document.getElementById("ch").indeterminate = true;
document.getElementById("unch").indeterminate = true;
var checkboxes = [document.getElementById("foo1"), var checkboxes = [document.getElementById("foo1"),
document.getElementById("foo2"), document.getElementById("foo2"),
document.getElementById("foo3")]; document.getElementById("foo3")];

View file

@ -4662,9 +4662,6 @@
[HTMLFormElement interface: attribute length] [HTMLFormElement interface: attribute length]
expected: FAIL expected: FAIL
[HTMLFormElement interface: operation reset()]
expected: PASS
[HTMLFormElement interface: operation checkValidity()] [HTMLFormElement interface: operation checkValidity()]
expected: FAIL expected: FAIL
@ -4680,9 +4677,6 @@
[HTMLFormElement interface: document.createElement("form") must inherit property "length" with the proper type (10)] [HTMLFormElement interface: document.createElement("form") must inherit property "length" with the proper type (10)]
expected: FAIL expected: FAIL
[HTMLFormElement interface: document.createElement("form") must inherit property "reset" with the proper type (14)]
expected: PASS
[HTMLFormElement interface: document.createElement("form") must inherit property "checkValidity" with the proper type (15)] [HTMLFormElement interface: document.createElement("form") must inherit property "checkValidity" with the proper type (15)]
expected: FAIL expected: FAIL
@ -4779,9 +4773,6 @@
[HTMLInputElement interface: attribute step] [HTMLInputElement interface: attribute step]
expected: FAIL expected: FAIL
[HTMLInputElement interface: attribute defaultValue]
expected: PASS
[HTMLInputElement interface: attribute valueAsDate] [HTMLInputElement interface: attribute valueAsDate]
expected: FAIL expected: FAIL
@ -4914,9 +4905,6 @@
[HTMLInputElement interface: document.createElement("input") must inherit property "step" with the proper type (31)] [HTMLInputElement interface: document.createElement("input") must inherit property "step" with the proper type (31)]
expected: FAIL expected: FAIL
[HTMLInputElement interface: document.createElement("input") must inherit property "defaultValue" with the proper type (33)]
expected: PASS
[HTMLInputElement interface: document.createElement("input") must inherit property "valueAsDate" with the proper type (35)] [HTMLInputElement interface: document.createElement("input") must inherit property "valueAsDate" with the proper type (35)]
expected: FAIL expected: FAIL

View file

@ -6261,135 +6261,6 @@
[input.step: IDL set to object "test-valueOf" followed by IDL get] [input.step: IDL set to object "test-valueOf" followed by IDL get]
expected: FAIL expected: FAIL
[input.defaultValue (<input value>): typeof IDL attribute]
expected: PASS
[input.defaultValue (<input value>): IDL get with DOM attribute unset]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to "" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo " followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to undefined followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to 7 followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to 1.5 followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to true followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to false followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to object "[object Object\]" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to NaN followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to Infinity followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to -Infinity followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to "\\0" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to null followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to object "test-toString" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): setAttribute() to object "test-valueOf" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to "" followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo " followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to undefined followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to undefined followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to 7 followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to 7 followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to 1.5 followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to 1.5 followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to true followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to true followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to false followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to false followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to object "[object Object\]" followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to object "[object Object\]" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to NaN followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to NaN followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to Infinity followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to Infinity followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to -Infinity followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to -Infinity followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to "\\0" followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to null followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to null followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to object "test-toString" followed by getAttribute()]
expected: PASS
[input.defaultValue (<input value>): IDL set to object "test-toString" followed by IDL get]
expected: PASS
[input.defaultValue (<input value>): IDL set to object "test-valueOf" followed by IDL get]
expected: PASS
[input.align: typeof IDL attribute] [input.align: typeof IDL attribute]
expected: FAIL expected: FAIL