Skip to content

Commit

Permalink
fixes #5603, adds support for tabindex
Browse files Browse the repository at this point in the history
  • Loading branch information
pgonda committed May 20, 2015
1 parent f554ab1 commit 1c96eed
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 16 deletions.
11 changes: 8 additions & 3 deletions components/script/dom/element.rs
Expand Up @@ -51,7 +51,7 @@ use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementHelper
use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementHelpers};
use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementHelpers};
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::{window_from_node};
use dom::nodelist::NodeList;
Expand Down Expand Up @@ -712,9 +712,11 @@ impl<'a> FocusElementHelpers for JSRef<'a, Element> {
return false;
}
// 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);
if node.get_flag(SEQUENTIALLY_FOCUSABLE) {
return true;
}
// https://html.spec.whatwg.org/multipage/#specially-focusable
match node.type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
Expand Down Expand Up @@ -934,6 +936,9 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {

self.attrs.borrow_mut().remove(idx);
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);
if node.is_in_doc() {
Expand Down
62 changes: 61 additions & 1 deletion components/script/dom/htmlelement.rs
Expand Up @@ -4,6 +4,7 @@

use dom::attr::Attr;
use dom::attr::AttrHelpers;
use dom::attr::AttrValue;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::HTMLElementBinding;
use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
Expand All @@ -24,7 +25,7 @@ use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::htmlinputelement::HTMLInputElement;
use dom::htmlmediaelement::HTMLMediaElementTypeId;
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::window::WindowHelpers;

Expand Down Expand Up @@ -70,13 +71,52 @@ impl HTMLElement {

trait PrivateHTMLElementHelpers {
fn is_body_or_frameset(self) -> bool;
fn update_sequentially_focusable_status(self);
}

impl<'a> PrivateHTMLElementHelpers for JSRef<'a, HTMLElement> {
fn is_body_or_frameset(self) -> bool {
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(self);
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> {
Expand Down Expand Up @@ -220,6 +260,19 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLElement> {
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>) {
if let Some(ref s) = self.super_type() {
s.after_set_attr(attr);
Expand All @@ -236,6 +289,13 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLElement> {
&name[2..],
(**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();
}
}

Expand Down
3 changes: 3 additions & 0 deletions components/script/dom/node.rs
Expand Up @@ -158,6 +158,9 @@ bitflags! {
const CLICK_IN_PROGRESS = 0x100,
#[doc = "Specifies whether this node has the focus."]
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,
}
}

Expand Down
8 changes: 8 additions & 0 deletions components/script/dom/virtualmethods.rs
Expand Up @@ -88,6 +88,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`
/// on this element.
fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
Expand Down

This file was deleted.

Expand Up @@ -3,12 +3,5 @@
[input3 has the attribute autofocus]
expected: FAIL

[tabindex attribute makes the element focusable]
expected: FAIL

[editable elements are focusable]
expected: FAIL

[':focus' matches focussed body with tabindex]
expected: FAIL

0 comments on commit 1c96eed

Please sign in to comment.