mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Address review comments
This commit is contained in:
parent
6482e313d6
commit
a2f7e0fbd6
4 changed files with 63 additions and 67 deletions
|
@ -10,9 +10,9 @@ use dom::attr::{AttrValue, StringAttrValue, UIntAttrValue, AtomAttrValue};
|
||||||
use dom::namednodemap::NamedNodeMap;
|
use dom::namednodemap::NamedNodeMap;
|
||||||
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::EventBinding::EventMethods;
|
|
||||||
use dom::bindings::codegen::Bindings::ElementBinding;
|
use dom::bindings::codegen::Bindings::ElementBinding;
|
||||||
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
|
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
|
||||||
|
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
||||||
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
|
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
|
||||||
use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived, HTMLTableCellElementDerived};
|
use dom::bindings::codegen::InheritTypes::{ElementDerived, HTMLInputElementDerived, HTMLTableCellElementDerived};
|
||||||
use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, NodeCast, EventTargetCast, ElementCast};
|
use dom::bindings::codegen::InheritTypes::{HTMLInputElementCast, NodeCast, EventTargetCast, ElementCast};
|
||||||
|
@ -60,6 +60,9 @@ pub struct Element {
|
||||||
style_attribute: DOMRefCell<Option<style::PropertyDeclarationBlock>>,
|
style_attribute: DOMRefCell<Option<style::PropertyDeclarationBlock>>,
|
||||||
attr_list: MutNullableJS<NamedNodeMap>,
|
attr_list: MutNullableJS<NamedNodeMap>,
|
||||||
class_list: MutNullableJS<DOMTokenList>,
|
class_list: MutNullableJS<DOMTokenList>,
|
||||||
|
// TODO: find a better place to keep this (#4105)
|
||||||
|
// https://critic.hoppipolla.co.uk/showcomment?chain=8873
|
||||||
|
// Perhaps using a Set in Document?
|
||||||
click_in_progress: Cell<bool>,
|
click_in_progress: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1193,7 +1196,7 @@ pub trait ActivationElementHelpers<'a> {
|
||||||
fn as_maybe_activatable(&'a self) -> Option<&'a Activatable + 'a>;
|
fn as_maybe_activatable(&'a self) -> Option<&'a Activatable + 'a>;
|
||||||
fn click_in_progress(self) -> bool;
|
fn click_in_progress(self) -> bool;
|
||||||
fn set_click_in_progress(self, click: bool);
|
fn set_click_in_progress(self, click: bool);
|
||||||
fn nearest_activable_element(self) -> Option<JSRef<'a, Element>>;
|
fn nearest_activable_element(self) -> Option<Temporary<Element>>;
|
||||||
fn authentic_click_activation<'b>(self, event: JSRef<'b, Event>);
|
fn authentic_click_activation<'b>(self, event: JSRef<'b, Event>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1220,14 +1223,15 @@ impl<'a> ActivationElementHelpers<'a> for JSRef<'a, Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/interaction.html#nearest-activatable-element
|
// https://html.spec.whatwg.org/multipage/interaction.html#nearest-activatable-element
|
||||||
fn nearest_activable_element(self) -> Option<JSRef<'a, Element>> {
|
fn nearest_activable_element(self) -> Option<Temporary<Element>> {
|
||||||
match self.as_maybe_activatable() {
|
match self.as_maybe_activatable() {
|
||||||
Some(el) => Some(*el.as_element().root()),
|
Some(el) => Some(Temporary::from_rooted(*el.as_element().root())),
|
||||||
None => {
|
None => {
|
||||||
let node: JSRef<Node> = NodeCast::from_ref(self);
|
let node: JSRef<Node> = NodeCast::from_ref(self);
|
||||||
node.ancestors()
|
node.ancestors()
|
||||||
.filter_map(|node| ElementCast::to_ref(node))
|
.filter_map(|node| ElementCast::to_ref(node))
|
||||||
.filter(|e| e.as_maybe_activatable().is_some()).next()
|
.filter(|e| e.as_maybe_activatable().is_some()).next()
|
||||||
|
.map(|r| Temporary::from_rooted(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1241,7 +1245,7 @@ impl<'a> ActivationElementHelpers<'a> for JSRef<'a, Element> {
|
||||||
fn authentic_click_activation<'b>(self, event: JSRef<'b, Event>) {
|
fn authentic_click_activation<'b>(self, event: JSRef<'b, Event>) {
|
||||||
// Not explicitly part of the spec, however this helps enforce the invariants
|
// Not explicitly part of the spec, however this helps enforce the invariants
|
||||||
// required to save state between pre-activation and post-activation
|
// required to save state between pre-activation and post-activation
|
||||||
// Since we cannot nest authentic clicks (unlike synthetic click activation, where
|
// since we cannot nest authentic clicks (unlike synthetic click activation, where
|
||||||
// the script can generate more click events from the handler)
|
// the script can generate more click events from the handler)
|
||||||
assert!(!self.click_in_progress());
|
assert!(!self.click_in_progress());
|
||||||
|
|
||||||
|
@ -1250,21 +1254,26 @@ impl<'a> ActivationElementHelpers<'a> for JSRef<'a, Element> {
|
||||||
// Step 3
|
// Step 3
|
||||||
self.set_click_in_progress(true);
|
self.set_click_in_progress(true);
|
||||||
// Step 4
|
// Step 4
|
||||||
let e = self.nearest_activable_element();
|
let e = self.nearest_activable_element().root();
|
||||||
// Step 5
|
match e {
|
||||||
e.map(|a| a.as_maybe_activatable().map(|el| el.pre_click_activation()));
|
Some (el) => match el.as_maybe_activatable() {
|
||||||
// Step 6
|
Some(elem) => {
|
||||||
|
// Step 5-6
|
||||||
|
elem.pre_click_activation();
|
||||||
target.dispatch_event_with_target(None, event).ok();
|
target.dispatch_event_with_target(None, event).ok();
|
||||||
e.map(|el| {
|
|
||||||
el.as_maybe_activatable().map(|a| {
|
|
||||||
if !event.DefaultPrevented() {
|
if !event.DefaultPrevented() {
|
||||||
// post click activation
|
// post click activation
|
||||||
a.activation_behavior();
|
elem.activation_behavior();
|
||||||
} else {
|
} else {
|
||||||
a.canceled_activation();
|
elem.canceled_activation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 6
|
||||||
|
None => {target.dispatch_event_with_target(None, event).ok();}
|
||||||
|
},
|
||||||
|
// Step 6
|
||||||
|
None => {target.dispatch_event_with_target(None, event).ok();}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
// Step 7
|
// Step 7
|
||||||
self.set_click_in_progress(false);
|
self.set_click_in_progress(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@ impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
let element: JSRef<Element> = ElementCast::from_ref(self);
|
let element: JSRef<Element> = ElementCast::from_ref(self);
|
||||||
|
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=27430 ?
|
||||||
element.as_maybe_activatable().map(|a| a.synthetic_click_activation(false, false, false, false));
|
element.as_maybe_activatable().map(|a| a.synthetic_click_activation(false, false, false, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ 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::DocumentBinding::DocumentMethods;
|
||||||
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
|
|
||||||
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::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, HTMLFormElementCast, HTMLInputElementCast, NodeCast};
|
||||||
|
@ -36,7 +36,6 @@ use string_cache::Atom;
|
||||||
|
|
||||||
use std::ascii::OwnedAsciiExt;
|
use std::ascii::OwnedAsciiExt;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
|
||||||
const DEFAULT_SUBMIT_VALUE: &'static str = "Submit";
|
const DEFAULT_SUBMIT_VALUE: &'static str = "Submit";
|
||||||
|
@ -65,7 +64,7 @@ pub struct HTMLInputElement {
|
||||||
indeterminate: Cell<bool>,
|
indeterminate: Cell<bool>,
|
||||||
size: Cell<u32>,
|
size: Cell<u32>,
|
||||||
textinput: DOMRefCell<TextInput>,
|
textinput: DOMRefCell<TextInput>,
|
||||||
activation_state: RefCell<InputActivationState>,
|
activation_state: DOMRefCell<InputActivationState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[jstraceable]
|
#[jstraceable]
|
||||||
|
@ -109,7 +108,7 @@ impl HTMLInputElement {
|
||||||
indeterminate: Cell::new(false),
|
indeterminate: Cell::new(false),
|
||||||
size: Cell::new(DEFAULT_INPUT_SIZE),
|
size: Cell::new(DEFAULT_INPUT_SIZE),
|
||||||
textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())),
|
textinput: DOMRefCell::new(TextInput::new(Single, "".to_string())),
|
||||||
activation_state: RefCell::new(InputActivationState::new())
|
activation_state: DOMRefCell::new(InputActivationState::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,14 +261,14 @@ pub trait HTMLInputElementHelpers {
|
||||||
fn force_relayout(self);
|
fn force_relayout(self);
|
||||||
fn radio_group_updated(self, group: Option<&str>);
|
fn radio_group_updated(self, group: Option<&str>);
|
||||||
fn get_radio_group_name(self) -> Option<String>;
|
fn get_radio_group_name(self) -> Option<String>;
|
||||||
fn get_radio_group_all(self, group: Option<&str>) -> Vec<Temporary<HTMLInputElement>>;
|
fn get_radio_group_members(self, group: Option<&str>) -> Vec<Temporary<HTMLInputElement>>;
|
||||||
fn update_checked_state(self, checked: bool);
|
fn update_checked_state(self, checked: bool);
|
||||||
fn get_size(&self) -> u32;
|
fn get_size(&self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) {
|
fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&str>) {
|
||||||
//TODO: if not in document, use root ancestor instead of document
|
//TODO: if not in document, use root ancestor instead of document
|
||||||
for r in broadcaster.get_radio_group_all(group).iter().filter(|r| *r.root() != broadcaster) {
|
for r in broadcaster.get_radio_group_members(group).iter().filter(|r| *r.root() != broadcaster) {
|
||||||
let radio = r.root();
|
let radio = r.root();
|
||||||
if radio.Checked() {
|
if radio.Checked() {
|
||||||
radio.SetChecked(false);
|
radio.SetChecked(false);
|
||||||
|
@ -299,7 +298,7 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-group
|
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-group
|
||||||
fn get_radio_group_all(self, group: Option<&str>) -> Vec<Temporary<HTMLInputElement>> {
|
fn get_radio_group_members(self, group: Option<&str>) -> Vec<Temporary<HTMLInputElement>> {
|
||||||
assert!(self.input_type.get() == InputRadio);
|
assert!(self.input_type.get() == InputRadio);
|
||||||
//TODO: if not in document, use root ancestor instead of document
|
//TODO: if not in document, use root ancestor instead of document
|
||||||
let doc = document_from_node(self).root();
|
let doc = document_from_node(self).root();
|
||||||
|
@ -312,7 +311,7 @@ impl<'a> HTMLInputElementHelpers for JSRef<'a, HTMLInputElement> {
|
||||||
r.form_owner() == owner &&
|
r.form_owner() == owner &&
|
||||||
// TODO should be a unicode compatibility caseless match
|
// TODO should be a unicode compatibility caseless match
|
||||||
match (r.get_radio_group_name(), group) {
|
match (r.get_radio_group_name(), group) {
|
||||||
(Some(ref s1), Some(s2)) if s1.as_slice() == s2 => true,
|
(Some(ref s1), Some(s2)) => s1.as_slice() == s2,
|
||||||
(None, None) => true,
|
(None, None) => true,
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
|
@ -562,8 +561,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
let mut cache = self.activation_state.borrow_mut();
|
let mut cache = self.activation_state.borrow_mut();
|
||||||
let ty = self.input_type.get();
|
let ty = self.input_type.get();
|
||||||
cache.old_type = ty;
|
cache.old_type = ty;
|
||||||
if self.mutable() {
|
cache.was_mutable = self.mutable();
|
||||||
cache.was_mutable = true;
|
if cache.was_mutable {
|
||||||
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
|
||||||
|
@ -578,15 +577,14 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
},
|
},
|
||||||
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):pre-click-activation-steps
|
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):pre-click-activation-steps
|
||||||
InputRadio => {
|
InputRadio => {
|
||||||
cache.checked_radio.assign(self.get_radio_group_all(self.get_radio_group_name().as_ref()
|
let members = self.get_radio_group_members(self.get_radio_group_name().as_ref()
|
||||||
.map(|s| s.as_slice()))
|
.map(|s| s.as_slice()));
|
||||||
.into_iter().find(|r| r.root().Checked()));
|
let checked_member = members.into_iter().find(|r| r.root().Checked());
|
||||||
|
cache.checked_radio.assign(checked_member);
|
||||||
self.SetChecked(true);
|
self.SetChecked(true);
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
cache.was_mutable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,8 +614,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
if cache.was_mutable {
|
if cache.was_mutable {
|
||||||
let old_checked: Option<Root<HTMLInputElement>> = cache.checked_radio.get().root();
|
let old_checked: Option<Root<HTMLInputElement>> = cache.checked_radio.get().root();
|
||||||
let name = self.get_radio_group_name();
|
let name = self.get_radio_group_name();
|
||||||
old_checked.map_or_else(|| self.SetChecked(false),
|
match old_checked {
|
||||||
|o| {
|
Some(o) => {
|
||||||
// Avoiding iterating through the whole tree here, instead
|
// Avoiding iterating through the whole tree here, instead
|
||||||
// we can check if the conditions for radio group siblings apply
|
// we can check if the conditions for radio group siblings apply
|
||||||
if name != None && // unless self no longer has a button group
|
if name != None && // unless self no longer has a button group
|
||||||
|
@ -629,7 +627,9 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
} else {
|
} else {
|
||||||
self.SetChecked(false);
|
self.SetChecked(false);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
None => self.SetChecked(false)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
|
@ -654,26 +654,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InputCheckbox => {
|
InputCheckbox | InputRadio => {
|
||||||
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):activation-behavior
|
// https://html.spec.whatwg.org/multipage/forms.html#checkbox-state-(type=checkbox):activation-behavior
|
||||||
if self.mutable() {
|
|
||||||
let win = window_from_node(*self).root();
|
|
||||||
let event = Event::new(&Window(*win),
|
|
||||||
"input".to_string(),
|
|
||||||
Bubbles, NotCancelable).root();
|
|
||||||
event.set_trusted(true);
|
|
||||||
let target: JSRef<EventTarget> = EventTargetCast::from_ref(*self);
|
|
||||||
target.DispatchEvent(*event).ok();
|
|
||||||
|
|
||||||
let event = Event::new(&Window(*win),
|
|
||||||
"change".to_string(),
|
|
||||||
Bubbles, NotCancelable).root();
|
|
||||||
event.set_trusted(true);
|
|
||||||
let target: JSRef<EventTarget> = EventTargetCast::from_ref(*self);
|
|
||||||
target.DispatchEvent(*event).ok();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
InputRadio => {
|
|
||||||
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):activation-behavior
|
// https://html.spec.whatwg.org/multipage/forms.html#radio-button-state-(type=radio):activation-behavior
|
||||||
if self.mutable() {
|
if self.mutable() {
|
||||||
let win = window_from_node(*self).root();
|
let win = window_from_node(*self).root();
|
||||||
|
@ -700,7 +682,7 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
fn implicit_submission(&self, ctrlKey: bool, shiftKey: bool, altKey: bool, metaKey: bool) {
|
fn implicit_submission(&self, ctrlKey: bool, shiftKey: bool, altKey: bool, metaKey: bool) {
|
||||||
let doc = document_from_node(*self).root();
|
let doc = document_from_node(*self).root();
|
||||||
// FIXME (#4082) use a custom iterator
|
// FIXME (#4082) use a custom iterator
|
||||||
let submits = doc.QuerySelectorAll("input[type=submit]".to_string()).unwrap().root();
|
doc.QuerySelectorAll("input[type=submit]".to_string()).root().map(|submits| {
|
||||||
// XXXManishearth there may be a more efficient way of doing this (#3553)
|
// XXXManishearth there may be a more efficient way of doing this (#3553)
|
||||||
let owner = self.form_owner();
|
let owner = self.form_owner();
|
||||||
if owner == None || ElementCast::from_ref(*self).click_in_progress() {
|
if owner == None || ElementCast::from_ref(*self).click_in_progress() {
|
||||||
|
@ -708,6 +690,8 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
|
||||||
}
|
}
|
||||||
submits.into_vec().iter()
|
submits.into_vec().iter()
|
||||||
.filter_map(|t| HTMLInputElementCast::to_ref(*t.root()))
|
.filter_map(|t| HTMLInputElementCast::to_ref(*t.root()))
|
||||||
.find(|r| r.form_owner() == owner).map(|s| s.synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey));
|
.find(|r| r.form_owner() == owner)
|
||||||
|
.map(|s| s.synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -894,6 +894,7 @@ impl ScriptTask {
|
||||||
let event = EventCast::from_ref(*keyevent);
|
let event = EventCast::from_ref(*keyevent);
|
||||||
let _ = target.DispatchEvent(event);
|
let _ = target.DispatchEvent(event);
|
||||||
let mut prevented = event.DefaultPrevented();
|
let mut prevented = event.DefaultPrevented();
|
||||||
|
|
||||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys-cancelable-keys
|
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keys-cancelable-keys
|
||||||
if state != Released && props.is_printable() && !prevented {
|
if state != Released && props.is_printable() && !prevented {
|
||||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keypress-event-order
|
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#keypress-event-order
|
||||||
|
@ -925,6 +926,7 @@ impl ScriptTask {
|
||||||
}
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.flush_layout();
|
window.flush_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue