mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Auto merge of #5858 - pgonda:tabindex-focus-flag, r=jdm
Added support for the tabindex field, also added its correct defaults (-2 TODOs for things not supported in Servo yet). Also added tabindex logic into Element::is_focusable_area. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/5858) <!-- Reviewable:end -->
This commit is contained in:
commit
fada39164c
6 changed files with 80 additions and 16 deletions
|
@ -54,7 +54,7 @@ use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelper
|
||||||
use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementHelpers};
|
use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementHelpers};
|
||||||
use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementHelpers};
|
use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementHelpers};
|
||||||
use dom::htmltextareaelement::{HTMLTextAreaElement, RawLayoutHTMLTextAreaElementHelpers};
|
use dom::htmltextareaelement::{HTMLTextAreaElement, RawLayoutHTMLTextAreaElementHelpers};
|
||||||
use dom::node::{CLICK_IN_PROGRESS, LayoutNodeHelpers, Node, NodeHelpers, NodeTypeId};
|
use dom::node::{CLICK_IN_PROGRESS, LayoutNodeHelpers, Node, NodeHelpers, NodeTypeId, SEQUENTIALLY_FOCUSABLE};
|
||||||
use dom::node::{document_from_node, NodeDamage};
|
use dom::node::{document_from_node, NodeDamage};
|
||||||
use dom::node::{window_from_node};
|
use dom::node::{window_from_node};
|
||||||
use dom::nodelist::NodeList;
|
use dom::nodelist::NodeList;
|
||||||
|
@ -757,9 +757,11 @@ impl<'a> FocusElementHelpers for JSRef<'a, Element> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO: Check whether the element is being rendered (i.e. not hidden).
|
// TODO: Check whether the element is being rendered (i.e. not hidden).
|
||||||
// TODO: Check the tabindex focus flag.
|
|
||||||
// https://html.spec.whatwg.org/multipage/#specially-focusable
|
|
||||||
let node: JSRef<Node> = NodeCast::from_ref(self);
|
let node: JSRef<Node> = NodeCast::from_ref(self);
|
||||||
|
if node.get_flag(SEQUENTIALLY_FOCUSABLE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// https://html.spec.whatwg.org/multipage/#specially-focusable
|
||||||
match node.type_id() {
|
match node.type_id() {
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
|
||||||
|
@ -979,6 +981,9 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
|
||||||
|
|
||||||
self.attrs.borrow_mut().remove(idx);
|
self.attrs.borrow_mut().remove(idx);
|
||||||
attr.r().set_owner(None);
|
attr.r().set_owner(None);
|
||||||
|
if attr.r().namespace() == &ns!("") {
|
||||||
|
vtable_for(&NodeCast::from_ref(self)).after_remove_attr(attr.r().name());
|
||||||
|
}
|
||||||
|
|
||||||
let node: JSRef<Node> = NodeCast::from_ref(self);
|
let node: JSRef<Node> = NodeCast::from_ref(self);
|
||||||
if node.is_in_doc() {
|
if node.is_in_doc() {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use dom::attr::Attr;
|
use dom::attr::Attr;
|
||||||
use dom::attr::AttrHelpers;
|
use dom::attr::AttrHelpers;
|
||||||
|
use dom::attr::AttrValue;
|
||||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
||||||
use dom::bindings::codegen::Bindings::HTMLElementBinding;
|
use dom::bindings::codegen::Bindings::HTMLElementBinding;
|
||||||
use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
|
||||||
|
@ -24,7 +25,7 @@ use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
|
||||||
use dom::htmlinputelement::HTMLInputElement;
|
use dom::htmlinputelement::HTMLInputElement;
|
||||||
use dom::htmlmediaelement::HTMLMediaElementTypeId;
|
use dom::htmlmediaelement::HTMLMediaElementTypeId;
|
||||||
use dom::htmltablecellelement::HTMLTableCellElementTypeId;
|
use dom::htmltablecellelement::HTMLTableCellElementTypeId;
|
||||||
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node};
|
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node, SEQUENTIALLY_FOCUSABLE};
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
use dom::window::WindowHelpers;
|
use dom::window::WindowHelpers;
|
||||||
|
|
||||||
|
@ -70,6 +71,7 @@ impl HTMLElement {
|
||||||
|
|
||||||
trait PrivateHTMLElementHelpers {
|
trait PrivateHTMLElementHelpers {
|
||||||
fn is_body_or_frameset(self) -> bool;
|
fn is_body_or_frameset(self) -> bool;
|
||||||
|
fn update_sequentially_focusable_status(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
|
impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
|
||||||
|
@ -77,6 +79,44 @@ impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
|
||||||
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(self);
|
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(self);
|
||||||
eventtarget.is_htmlbodyelement() || eventtarget.is_htmlframesetelement()
|
eventtarget.is_htmlbodyelement() || eventtarget.is_htmlframesetelement()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_sequentially_focusable_status(self) {
|
||||||
|
let element = ElementCast::from_ref(self);
|
||||||
|
let node = NodeCast::from_ref(self);
|
||||||
|
if element.has_attribute(&atom!("tabindex")) {
|
||||||
|
node.set_flag(SEQUENTIALLY_FOCUSABLE, true);
|
||||||
|
} else {
|
||||||
|
match node.type_id() {
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement)) |
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement))
|
||||||
|
=> node.set_flag(SEQUENTIALLY_FOCUSABLE, true),
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) |
|
||||||
|
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) => {
|
||||||
|
if element.has_attribute(&atom!("href")) {
|
||||||
|
node.set_flag(SEQUENTIALLY_FOCUSABLE, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
if let Some(attr) = element.get_attribute(&ns!(""), &atom!("draggable")) {
|
||||||
|
let attr = attr.root();
|
||||||
|
let attr = attr.r();
|
||||||
|
let value = attr.value();
|
||||||
|
let is_true = match *value {
|
||||||
|
AttrValue::String(ref string) => string == "true",
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
node.set_flag(SEQUENTIALLY_FOCUSABLE, is_true);
|
||||||
|
} else {
|
||||||
|
node.set_flag(SEQUENTIALLY_FOCUSABLE, false);
|
||||||
|
}
|
||||||
|
//TODO set SEQUENTIALLY_FOCUSABLE flag if editing host
|
||||||
|
//TODO set SEQUENTIALLY_FOCUSABLE flag if "sorting interface th elements"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
|
impl<'a> HTMLElementMethods for JSRef<'a, HTMLElement> {
|
||||||
|
@ -220,6 +260,19 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLElement> {
|
||||||
Some(element as &VirtualMethods)
|
Some(element as &VirtualMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn before_remove_attr(&self, attr: JSRef<Attr>) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.before_remove_attr(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn after_remove_attr(&self, name: &Atom) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.after_remove_attr(name);
|
||||||
|
}
|
||||||
|
self.update_sequentially_focusable_status();
|
||||||
|
}
|
||||||
|
|
||||||
fn after_set_attr(&self, attr: JSRef<Attr>) {
|
fn after_set_attr(&self, attr: JSRef<Attr>) {
|
||||||
if let Some(ref s) = self.super_type() {
|
if let Some(ref s) = self.super_type() {
|
||||||
s.after_set_attr(attr);
|
s.after_set_attr(attr);
|
||||||
|
@ -236,6 +289,13 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLElement> {
|
||||||
&name[2..],
|
&name[2..],
|
||||||
(**attr.value()).to_owned());
|
(**attr.value()).to_owned());
|
||||||
}
|
}
|
||||||
|
self.update_sequentially_focusable_status();
|
||||||
|
}
|
||||||
|
fn bind_to_tree(&self, tree_in_doc: bool) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.bind_to_tree(tree_in_doc);
|
||||||
|
}
|
||||||
|
self.update_sequentially_focusable_status();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,9 @@ bitflags! {
|
||||||
const CLICK_IN_PROGRESS = 0x100,
|
const CLICK_IN_PROGRESS = 0x100,
|
||||||
#[doc = "Specifies whether this node has the focus."]
|
#[doc = "Specifies whether this node has the focus."]
|
||||||
const IN_FOCUS_STATE = 0x200,
|
const IN_FOCUS_STATE = 0x200,
|
||||||
|
#[doc = "Specifies whether this node is focusable and whether it is supposed \
|
||||||
|
to be reachable with using sequential focus navigation."]
|
||||||
|
const SEQUENTIALLY_FOCUSABLE = 0x400,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,14 @@ pub trait VirtualMethods {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when changing or removing attributes, after all modification
|
||||||
|
/// has taken place.
|
||||||
|
fn after_remove_attr(&self, name: &Atom) {
|
||||||
|
if let Some(ref s) = self.super_type() {
|
||||||
|
s.after_remove_attr(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the right AttrValue variant for the attribute with name `name`
|
/// Returns the right AttrValue variant for the attribute with name `name`
|
||||||
/// on this element.
|
/// on this element.
|
||||||
fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
|
fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[disabledElement.html]
|
|
||||||
type: testharness
|
|
||||||
[A disabled <span> should be focusable]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -3,12 +3,5 @@
|
||||||
[input3 has the attribute autofocus]
|
[input3 has the attribute autofocus]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[tabindex attribute makes the element focusable]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[editable elements are focusable]
|
[editable elements are focusable]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[':focus' matches focussed body with tabindex]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue