aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/element.rs11
-rw-r--r--components/script/dom/htmlelement.rs62
-rw-r--r--components/script/dom/node.rs3
-rw-r--r--components/script/dom/virtualmethods.rs8
-rw-r--r--tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini5
-rw-r--r--tests/wpt/metadata/html/semantics/selectors/pseudo-classes/focus.html.ini7
6 files changed, 80 insertions, 16 deletions
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index a666a28e627..b586172816d 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -54,7 +54,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;
@@ -757,9 +757,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)) |
@@ -979,6 +981,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() {
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index ce70535de74..b3ffe4b056f 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -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;
@@ -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;
@@ -70,6 +71,7 @@ impl HTMLElement {
trait PrivateHTMLElementHelpers {
fn is_body_or_frameset(self) -> bool;
+ fn update_sequentially_focusable_status(self);
}
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);
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> {
@@ -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);
@@ -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();
}
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index ec707cd3e02..92ed3f41f6e 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -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,
}
}
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
index a83c801831d..4effc4daf33 100644
--- a/components/script/dom/virtualmethods.rs
+++ b/components/script/dom/virtualmethods.rs
@@ -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`
/// on this element.
fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
diff --git a/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini b/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini
deleted file mode 100644
index cffbca16463..00000000000
--- a/tests/wpt/metadata/html/semantics/disabled-elements/disabledElement.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[disabledElement.html]
- type: testharness
- [A disabled <span> should be focusable]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/semantics/selectors/pseudo-classes/focus.html.ini b/tests/wpt/metadata/html/semantics/selectors/pseudo-classes/focus.html.ini
index f3529fc646d..387c512ce75 100644
--- a/tests/wpt/metadata/html/semantics/selectors/pseudo-classes/focus.html.ini
+++ b/tests/wpt/metadata/html/semantics/selectors/pseudo-classes/focus.html.ini
@@ -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
-