Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements basic form resetting #4133

Merged
merged 12 commits into from Dec 16, 2014

Implements :indeterminate pseudo-class

Addresses reviews
  • Loading branch information
mttr committed Dec 16, 2014
commit 504f968b20b35f901f2f6af564023b2bc8aa998d
@@ -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]
fn has_class(self, name: &Atom) -> bool {
unsafe {
@@ -35,7 +35,7 @@ use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers};
use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementHelpers};
use dom::htmlcollection::HTMLCollection;
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers};
use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers, HTMLInputElementHelpers};
use dom::htmlserializer::serialize;
use dom::htmltableelement::{HTMLTableElement, HTMLTableElementHelpers};
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelpers};
@@ -211,6 +211,7 @@ pub trait RawLayoutElementHelpers {
unsafe fn get_integer_attribute_for_layout(&self, integer_attribute: IntegerAttribute)
-> Option<i32>;
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)
-> Option<u32>;
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()
}

#[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,
attribute: UnsignedIntegerAttribute)
-> Option<u32> {
@@ -1274,6 +1287,12 @@ impl<'a> style::TElement<'a> for JSRef<'a, Element> {
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 {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
@@ -2,15 +2,16 @@
* 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::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::HTMLFormElementBinding;
use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
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::js::{JSRef, Temporary};
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::{Document, DocumentHelpers};
use dom::element::{Element, AttributeHandlers, HTMLFormElementTypeId, HTMLTextAreaElementTypeId, HTMLDataListElementTypeId};
@@ -31,9 +32,12 @@ use url::UrlParser;
use url::form_urlencoded::serialize;
use string_cache::Atom;

use std::cell::Cell;

#[dom_struct]
pub struct HTMLFormElement {
htmlelement: HTMLElement,
marked_for_reset: Cell<bool>,
}

impl HTMLFormElementDerived for EventTarget {
@@ -45,7 +49,8 @@ impl HTMLFormElementDerived for EventTarget {
impl HTMLFormElement {
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> 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) {
// 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 event = Event::new(Window(*win),
"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 {
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,
attr: &Atom,
input: |Self| -> DOMString,
@@ -7,12 +7,11 @@ use dom::attr::{Attr, AttrValue, UIntAttrValue};
use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell;
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::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
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::KeyboardEventCast;
use dom::bindings::global::Window;
@@ -134,6 +133,7 @@ pub trait LayoutHTMLInputElementHelpers {

pub trait RawLayoutHTMLInputElementHelpers {
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;
}

@@ -176,6 +176,11 @@ impl RawLayoutHTMLInputElementHelpers for HTMLInputElement {
self.checked.get()
}

#[allow(unrooted_must_root)]
unsafe fn get_indeterminate_state_for_layout(&self) -> bool {
self.indeterminate.get()
}

#[allow(unrooted_must_root)]
unsafe fn get_size_for_layout(&self) -> u32 {
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
fn SetIndeterminate(self, val: bool) {
// FIXME #4079 this should change the appearance
self.indeterminate.set(val)
}
}
@@ -295,6 +299,7 @@ pub trait HTMLInputElementHelpers {
fn get_radio_group_name(self) -> Option<String>;
fn update_checked_state(self, checked: bool, dirty: bool);
fn get_size(&self) -> u32;
fn get_indeterminate_state(self) -> bool;
}

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 {
self.size.get()
}

fn get_indeterminate_state(self) -> bool {
self.indeterminate.get()
}
}

impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
@@ -566,30 +575,6 @@ impl Reflectable for 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> {
ElementCast::from_ref(self)
}
@@ -601,6 +586,7 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLInputElement> {
!(self.Disabled() || self.ReadOnly())
}

// https://html.spec.whatwg.org/multipage/forms.html#the-input-element:concept-form-reset-control
fn reset(self) {
match self.input_type.get() {
InputRadio | InputCheckbox => {
@@ -632,6 +618,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
match ty {
// https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior
// InputSubmit => (), // No behavior defined
// https://html.spec.whatwg.org/multipage/forms.html#reset-button-state-(type=reset):activation-behavior
// InputSubmit => (), // No behavior defined
InputCheckbox => {
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):pre-click-activation-steps
// cache current values of `checked` and `indeterminate`
@@ -679,6 +667,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
match ty {
// https://html.spec.whatwg.org/multipage/forms.html#submit-button-state-(type=submit):activation-behavior
// 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
InputCheckbox => {
// We want to restore state only if the element had been changed in the first place
@@ -5,22 +5,21 @@
use dom::attr::{Attr, AttrValue};
use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
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::utils::{Reflectable, Reflector};
use dom::document::{Document, DocumentHelpers};
use dom::element::{AttributeHandlers, HTMLTextAreaElementTypeId, Element};
use dom::event::Event;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmlformelement::FormControl;
use dom::keyboardevent::KeyboardEvent;
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, OtherNodeDamage, ElementNodeTypeId};
use dom::node::{document_from_node};
@@ -299,30 +298,6 @@ impl Reflectable for 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> {
ElementCast::from_ref(self)
}
@@ -334,6 +309,7 @@ impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> {
}

fn reset(self) {
// https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-form-reset-control
self.SetValue(self.DefaultValue());
self.value_changed.set(false);
}
@@ -47,6 +47,7 @@ pub trait TElement<'a> : Copy {
fn get_disabled_state(self) -> bool;
fn get_enabled_state(self) -> bool;
fn get_checked_state(self) -> bool;
fn get_indeterminate_state(self) -> bool;
fn has_class(self, name: &Atom) -> bool;
fn has_nonzero_border(self) -> bool;

@@ -23,7 +23,7 @@ use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use selectors::{After, AnyLink, AttrDashMatch, AttrEqual};
use selectors::{AttrExists, AttrIncludes, AttrPrefixMatch};
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::{Hover, IDSelector, LastChild, LastOfType};
use selectors::{LaterSibling, LocalName, LocalNameSelector};
@@ -972,6 +972,12 @@ pub fn matches_simple_selector<'a,E,N>(selector: &SimpleSelector,
let elem = element.as_element();
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 => {
*shareable = false;
matches_first_child(element)
@@ -75,6 +75,7 @@ pub enum SimpleSelector {
Disabled,
Enabled,
Checked,
Indeterminate,
FirstChild, LastChild, OnlyChild,
// Empty,
Root,
@@ -167,7 +168,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
| &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..)
| &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..)
| &AnyLink | &Link | &Visited | &Hover | &Disabled | &Enabled
| &FirstChild | &LastChild | &OnlyChild | &Root | &Checked
| &FirstChild | &LastChild | &OnlyChild | &Root | &Checked | &Indeterminate
// | &Empty | &Lang(*)
| &NthChild(..) | &NthLastChild(..)
| &NthOfType(..) | &NthLastOfType(..)
@@ -568,6 +569,7 @@ fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<Simp
"disabled" => Ok(Disabled),
"enabled" => Ok(Enabled),
"checked" => Ok(Checked),
"indeterminate" => Ok(Indeterminate),
"first-child" => Ok(FirstChild),
"last-child" => Ok(LastChild),
"only-child" => Ok(OnlyChild),
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.