mirror of
https://github.com/servo/servo.git
synced 2025-08-02 04:00:32 +01:00
Make clicking on an element request focus for focusable elements.
This commit is contained in:
parent
a34d1573b6
commit
757371f4f0
5 changed files with 46 additions and 24 deletions
|
@ -204,6 +204,16 @@ pub enum IsHTMLDocument {
|
||||||
NonHTMLDocument,
|
NonHTMLDocument,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
#[unrooted_must_root_lint::must_root]
|
||||||
|
enum FocusTransaction {
|
||||||
|
/// No focus operation is in effect.
|
||||||
|
NotInTransaction,
|
||||||
|
/// A focus operation is in effect.
|
||||||
|
/// Contains the element that has most recently requested focus for itself.
|
||||||
|
InTransaction(Option<Dom<Element>>),
|
||||||
|
}
|
||||||
|
|
||||||
/// <https://dom.spec.whatwg.org/#document>
|
/// <https://dom.spec.whatwg.org/#document>
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct Document {
|
pub struct Document {
|
||||||
|
@ -243,8 +253,8 @@ pub struct Document {
|
||||||
ready_state: Cell<DocumentReadyState>,
|
ready_state: Cell<DocumentReadyState>,
|
||||||
/// Whether the DOMContentLoaded event has already been dispatched.
|
/// Whether the DOMContentLoaded event has already been dispatched.
|
||||||
domcontentloaded_dispatched: Cell<bool>,
|
domcontentloaded_dispatched: Cell<bool>,
|
||||||
/// The element that has most recently requested focus for itself.
|
/// The state of this document's focus transaction.
|
||||||
possibly_focused: MutNullableDom<Element>,
|
focus_transaction: DomRefCell<FocusTransaction>,
|
||||||
/// The element that currently has the document focus context.
|
/// The element that currently has the document focus context.
|
||||||
focused: MutNullableDom<Element>,
|
focused: MutNullableDom<Element>,
|
||||||
/// The script element that is currently executing.
|
/// The script element that is currently executing.
|
||||||
|
@ -1011,21 +1021,41 @@ impl Document {
|
||||||
|
|
||||||
/// Initiate a new round of checking for elements requesting focus. The last element to call
|
/// Initiate a new round of checking for elements requesting focus. The last element to call
|
||||||
/// `request_focus` before `commit_focus_transaction` is called will receive focus.
|
/// `request_focus` before `commit_focus_transaction` is called will receive focus.
|
||||||
pub fn begin_focus_transaction(&self) {
|
fn begin_focus_transaction(&self) {
|
||||||
self.possibly_focused.set(None);
|
*self.focus_transaction.borrow_mut() = FocusTransaction::InTransaction(Default::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request that the given element receive focus once the current transaction is complete.
|
/// Request that the given element receive focus once the current transaction is complete.
|
||||||
pub fn request_focus(&self, elem: &Element) {
|
/// If None is passed, then whatever element is currently focused will no longer be focused
|
||||||
if elem.is_focusable_area() {
|
/// once the transaction is complete.
|
||||||
self.possibly_focused.set(Some(elem))
|
pub(crate) fn request_focus(&self, elem: Option<&Element>, focus_type: FocusType) {
|
||||||
|
let implicit_transaction = matches!(
|
||||||
|
*self.focus_transaction.borrow(),
|
||||||
|
FocusTransaction::NotInTransaction
|
||||||
|
);
|
||||||
|
if implicit_transaction {
|
||||||
|
self.begin_focus_transaction();
|
||||||
|
}
|
||||||
|
if elem.map_or(true, |e| e.is_focusable_area()) {
|
||||||
|
*self.focus_transaction.borrow_mut() =
|
||||||
|
FocusTransaction::InTransaction(elem.map(Dom::from_ref));
|
||||||
|
}
|
||||||
|
if implicit_transaction {
|
||||||
|
self.commit_focus_transaction(focus_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reassign the focus context to the element that last requested focus during this
|
/// Reassign the focus context to the element that last requested focus during this
|
||||||
/// transaction, or none if no elements requested it.
|
/// transaction, or none if no elements requested it.
|
||||||
pub fn commit_focus_transaction(&self, focus_type: FocusType) {
|
fn commit_focus_transaction(&self, focus_type: FocusType) {
|
||||||
if self.focused == self.possibly_focused.get().as_deref() {
|
let possibly_focused = match *self.focus_transaction.borrow() {
|
||||||
|
FocusTransaction::NotInTransaction => unreachable!(),
|
||||||
|
FocusTransaction::InTransaction(ref elem) => {
|
||||||
|
elem.as_ref().map(|e| DomRoot::from_ref(&**e))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
*self.focus_transaction.borrow_mut() = FocusTransaction::NotInTransaction;
|
||||||
|
if self.focused == possibly_focused.as_ref().map(|e| &**e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(ref elem) = self.focused.get() {
|
if let Some(ref elem) = self.focused.get() {
|
||||||
|
@ -1040,7 +1070,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.focused.set(self.possibly_focused.get().as_deref());
|
self.focused.set(possibly_focused.as_ref().map(|e| &**e));
|
||||||
|
|
||||||
if let Some(ref elem) = self.focused.get() {
|
if let Some(ref elem) = self.focused.get() {
|
||||||
elem.set_focus_state(true);
|
elem.set_focus_state(true);
|
||||||
|
@ -1140,6 +1170,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.begin_focus_transaction();
|
self.begin_focus_transaction();
|
||||||
|
self.request_focus(Some(&*el), FocusType::Element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/uievents/#event-type-click
|
// https://w3c.github.io/uievents/#event-type-click
|
||||||
|
@ -2980,7 +3011,7 @@ impl Document {
|
||||||
stylesheet_list: MutNullableDom::new(None),
|
stylesheet_list: MutNullableDom::new(None),
|
||||||
ready_state: Cell::new(ready_state),
|
ready_state: Cell::new(ready_state),
|
||||||
domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
|
domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched),
|
||||||
possibly_focused: Default::default(),
|
focus_transaction: DomRefCell::new(FocusTransaction::NotInTransaction),
|
||||||
focused: Default::default(),
|
focused: Default::default(),
|
||||||
current_script: Default::default(),
|
current_script: Default::default(),
|
||||||
pending_parsing_blocking_script: Default::default(),
|
pending_parsing_blocking_script: Default::default(),
|
||||||
|
|
|
@ -411,9 +411,7 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
// TODO: Mark the element as locked for focus and run the focusing steps.
|
// TODO: Mark the element as locked for focus and run the focusing steps.
|
||||||
// https://html.spec.whatwg.org/multipage/#focusing-steps
|
// https://html.spec.whatwg.org/multipage/#focusing-steps
|
||||||
let document = document_from_node(self);
|
let document = document_from_node(self);
|
||||||
document.begin_focus_transaction();
|
document.request_focus(Some(self.upcast()), FocusType::Element);
|
||||||
document.request_focus(self.upcast());
|
|
||||||
document.commit_focus_transaction(FocusType::Element);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-blur
|
// https://html.spec.whatwg.org/multipage/#dom-blur
|
||||||
|
@ -424,9 +422,7 @@ impl HTMLElementMethods for HTMLElement {
|
||||||
}
|
}
|
||||||
// https://html.spec.whatwg.org/multipage/#unfocusing-steps
|
// https://html.spec.whatwg.org/multipage/#unfocusing-steps
|
||||||
let document = document_from_node(self);
|
let document = document_from_node(self);
|
||||||
document.begin_focus_transaction();
|
document.request_focus(None, FocusType::Element);
|
||||||
// If `request_focus` is not called, focus will be set to None.
|
|
||||||
document.commit_focus_transaction(FocusType::Element);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent
|
// https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent
|
||||||
|
|
|
@ -2520,7 +2520,6 @@ impl VirtualMethods for HTMLInputElement {
|
||||||
|
|
||||||
//TODO: set the editing position for text inputs
|
//TODO: set the editing position for text inputs
|
||||||
|
|
||||||
document_from_node(self).request_focus(self.upcast());
|
|
||||||
if self.input_type().is_textual_or_password() &&
|
if self.input_type().is_textual_or_password() &&
|
||||||
// Check if we display a placeholder. Layout doesn't know about this.
|
// Check if we display a placeholder. Layout doesn't know about this.
|
||||||
!self.textinput.borrow().is_empty()
|
!self.textinput.borrow().is_empty()
|
||||||
|
|
|
@ -23,7 +23,7 @@ use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
|
||||||
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
|
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
|
||||||
use crate::dom::htmlinputelement::HTMLInputElement;
|
use crate::dom::htmlinputelement::HTMLInputElement;
|
||||||
use crate::dom::keyboardevent::KeyboardEvent;
|
use crate::dom::keyboardevent::KeyboardEvent;
|
||||||
use crate::dom::node::{document_from_node, window_from_node};
|
use crate::dom::node::window_from_node;
|
||||||
use crate::dom::node::{
|
use crate::dom::node::{
|
||||||
BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext,
|
BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext,
|
||||||
};
|
};
|
||||||
|
@ -606,8 +606,6 @@ impl VirtualMethods for HTMLTextAreaElement {
|
||||||
|
|
||||||
if event.type_() == atom!("click") && !event.DefaultPrevented() {
|
if event.type_() == atom!("click") && !event.DefaultPrevented() {
|
||||||
//TODO: set the editing position for text inputs
|
//TODO: set the editing position for text inputs
|
||||||
|
|
||||||
document_from_node(self).request_focus(self.upcast());
|
|
||||||
} else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
|
} else if event.type_() == atom!("keydown") && !event.DefaultPrevented() {
|
||||||
if let Some(kevent) = event.downcast::<KeyboardEvent>() {
|
if let Some(kevent) = event.downcast::<KeyboardEvent>() {
|
||||||
// This can't be inlined, as holding on to textinput.borrow_mut()
|
// This can't be inlined, as holding on to textinput.borrow_mut()
|
||||||
|
|
|
@ -2504,9 +2504,7 @@ impl ScriptThread {
|
||||||
let frame_element = doc.find_iframe(browsing_context_id);
|
let frame_element = doc.find_iframe(browsing_context_id);
|
||||||
|
|
||||||
if let Some(ref frame_element) = frame_element {
|
if let Some(ref frame_element) = frame_element {
|
||||||
doc.begin_focus_transaction();
|
doc.request_focus(Some(frame_element.upcast()), FocusType::Parent);
|
||||||
doc.request_focus(frame_element.upcast());
|
|
||||||
doc.commit_focus_transaction(FocusType::Parent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue