diff options
-rw-r--r-- | components/script/dom/document.rs | 53 | ||||
-rw-r--r-- | components/script/dom/htmlelement.rs | 8 | ||||
-rwxr-xr-x | components/script/dom/htmlinputelement.rs | 1 | ||||
-rwxr-xr-x | components/script/dom/htmltextareaelement.rs | 4 | ||||
-rw-r--r-- | components/script/script_thread.rs | 4 |
5 files changed, 46 insertions, 24 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 157829412ce..97ca60af115 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -204,6 +204,16 @@ pub enum IsHTMLDocument { 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> #[dom_struct] pub struct Document { @@ -243,8 +253,8 @@ pub struct Document { ready_state: Cell<DocumentReadyState>, /// Whether the DOMContentLoaded event has already been dispatched. domcontentloaded_dispatched: Cell<bool>, - /// The element that has most recently requested focus for itself. - possibly_focused: MutNullableDom<Element>, + /// The state of this document's focus transaction. + focus_transaction: DomRefCell<FocusTransaction>, /// The element that currently has the document focus context. focused: MutNullableDom<Element>, /// 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 /// `request_focus` before `commit_focus_transaction` is called will receive focus. - pub fn begin_focus_transaction(&self) { - self.possibly_focused.set(None); + fn begin_focus_transaction(&self) { + *self.focus_transaction.borrow_mut() = FocusTransaction::InTransaction(Default::default()); } /// Request that the given element receive focus once the current transaction is complete. - pub fn request_focus(&self, elem: &Element) { - if elem.is_focusable_area() { - self.possibly_focused.set(Some(elem)) + /// If None is passed, then whatever element is currently focused will no longer be focused + /// once the transaction is complete. + 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 /// transaction, or none if no elements requested it. - pub fn commit_focus_transaction(&self, focus_type: FocusType) { - if self.focused == self.possibly_focused.get().as_deref() { + fn commit_focus_transaction(&self, focus_type: FocusType) { + 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; } 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() { elem.set_focus_state(true); @@ -1140,6 +1170,7 @@ impl Document { } self.begin_focus_transaction(); + self.request_focus(Some(&*el), FocusType::Element); } // https://w3c.github.io/uievents/#event-type-click @@ -2980,7 +3011,7 @@ impl Document { stylesheet_list: MutNullableDom::new(None), ready_state: Cell::new(ready_state), domcontentloaded_dispatched: Cell::new(domcontentloaded_dispatched), - possibly_focused: Default::default(), + focus_transaction: DomRefCell::new(FocusTransaction::NotInTransaction), focused: Default::default(), current_script: Default::default(), pending_parsing_blocking_script: Default::default(), diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 4c4d86f1642..1820c4367d5 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -411,9 +411,7 @@ impl HTMLElementMethods for HTMLElement { // TODO: Mark the element as locked for focus and run the focusing steps. // https://html.spec.whatwg.org/multipage/#focusing-steps let document = document_from_node(self); - document.begin_focus_transaction(); - document.request_focus(self.upcast()); - document.commit_focus_transaction(FocusType::Element); + document.request_focus(Some(self.upcast()), FocusType::Element); } // https://html.spec.whatwg.org/multipage/#dom-blur @@ -424,9 +422,7 @@ impl HTMLElementMethods for HTMLElement { } // https://html.spec.whatwg.org/multipage/#unfocusing-steps let document = document_from_node(self); - document.begin_focus_transaction(); - // If `request_focus` is not called, focus will be set to None. - document.commit_focus_transaction(FocusType::Element); + document.request_focus(None, FocusType::Element); } // https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 6044048b0d7..6177d4f7831 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -2520,7 +2520,6 @@ impl VirtualMethods for HTMLInputElement { //TODO: set the editing position for text inputs - document_from_node(self).request_focus(self.upcast()); if self.input_type().is_textual_or_password() && // Check if we display a placeholder. Layout doesn't know about this. !self.textinput.borrow().is_empty() diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index b7d224db123..cbdbcec2f0f 100755 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -23,7 +23,7 @@ use crate::dom::htmlfieldsetelement::HTMLFieldSetElement; use crate::dom::htmlformelement::{FormControl, HTMLFormElement}; use crate::dom::htmlinputelement::HTMLInputElement; 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::{ BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext, }; @@ -606,8 +606,6 @@ impl VirtualMethods for HTMLTextAreaElement { if event.type_() == atom!("click") && !event.DefaultPrevented() { //TODO: set the editing position for text inputs - - document_from_node(self).request_focus(self.upcast()); } else if event.type_() == atom!("keydown") && !event.DefaultPrevented() { if let Some(kevent) = event.downcast::<KeyboardEvent>() { // This can't be inlined, as holding on to textinput.borrow_mut() diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 729fe1595bc..61f597468f8 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -2504,9 +2504,7 @@ impl ScriptThread { let frame_element = doc.find_iframe(browsing_context_id); if let Some(ref frame_element) = frame_element { - doc.begin_focus_transaction(); - doc.request_focus(frame_element.upcast()); - doc.commit_focus_transaction(FocusType::Parent); + doc.request_focus(Some(frame_element.upcast()), FocusType::Parent); } } |