@@ -204,6 +204,16 @@ pub enum IsHTMLDocument {
204204 NonHTMLDocument ,
205205}
206206
207+ #[ derive( JSTraceable , MallocSizeOf ) ]
208+ #[ unrooted_must_root_lint:: must_root]
209+ enum FocusTransaction {
210+ /// No focus operation is in effect.
211+ NotInTransaction ,
212+ /// A focus operation is in effect.
213+ /// Contains the element that has most recently requested focus for itself.
214+ InTransaction ( Option < Dom < Element > > ) ,
215+ }
216+
207217/// <https://dom.spec.whatwg.org/#document>
208218#[ dom_struct]
209219pub struct Document {
@@ -243,8 +253,8 @@ pub struct Document {
243253 ready_state : Cell < DocumentReadyState > ,
244254 /// Whether the DOMContentLoaded event has already been dispatched.
245255 domcontentloaded_dispatched : Cell < bool > ,
246- /// The element that has most recently requested focus for itself .
247- possibly_focused : MutNullableDom < Element > ,
256+ /// The state of this document's focus transaction .
257+ focus_transaction : DomRefCell < FocusTransaction > ,
248258 /// The element that currently has the document focus context.
249259 focused : MutNullableDom < Element > ,
250260 /// The script element that is currently executing.
@@ -1011,21 +1021,52 @@ impl Document {
10111021
10121022 /// Initiate a new round of checking for elements requesting focus. The last element to call
10131023 /// `request_focus` before `commit_focus_transaction` is called will receive focus.
1014- pub fn begin_focus_transaction ( & self ) {
1015- self . possibly_focused . set ( None ) ;
1024+ fn begin_focus_transaction ( & self ) {
1025+ * self . focus_transaction . borrow_mut ( ) = FocusTransaction :: InTransaction ( Default :: default ( ) ) ;
1026+ }
1027+
1028+ /// <https://html.spec.whatwg.org/multipage/#focus-fixup-rule>
1029+ pub ( crate ) fn perform_focus_fixup_rule ( & self , not_focusable : & Element ) {
1030+ if Some ( not_focusable) != self . focused . get ( ) . as_ref ( ) . map ( |e| & * * e) {
1031+ return ;
1032+ }
1033+ self . request_focus (
1034+ self . GetBody ( ) . as_ref ( ) . map ( |e| & * e. upcast ( ) ) ,
1035+ FocusType :: Element ,
1036+ )
10161037 }
10171038
10181039 /// Request that the given element receive focus once the current transaction is complete.
1019- pub fn request_focus ( & self , elem : & Element ) {
1020- if elem. is_focusable_area ( ) {
1021- self . possibly_focused . set ( Some ( elem) )
1040+ /// If None is passed, then whatever element is currently focused will no longer be focused
1041+ /// once the transaction is complete.
1042+ pub ( crate ) fn request_focus ( & self , elem : Option < & Element > , focus_type : FocusType ) {
1043+ let implicit_transaction = matches ! (
1044+ * self . focus_transaction. borrow( ) ,
1045+ FocusTransaction :: NotInTransaction
1046+ ) ;
1047+ if implicit_transaction {
1048+ self . begin_focus_transaction ( ) ;
1049+ }
1050+ if elem. map_or ( true , |e| e. is_focusable_area ( ) ) {
1051+ * self . focus_transaction . borrow_mut ( ) =
1052+ FocusTransaction :: InTransaction ( elem. map ( Dom :: from_ref) ) ;
1053+ }
1054+ if implicit_transaction {
1055+ self . commit_focus_transaction ( focus_type) ;
10221056 }
10231057 }
10241058
10251059 /// Reassign the focus context to the element that last requested focus during this
10261060 /// transaction, or none if no elements requested it.
1027- pub fn commit_focus_transaction ( & self , focus_type : FocusType ) {
1028- if self . focused == self . possibly_focused . get ( ) . as_deref ( ) {
1061+ fn commit_focus_transaction ( & self , focus_type : FocusType ) {
1062+ let possibly_focused = match * self . focus_transaction . borrow ( ) {
1063+ FocusTransaction :: NotInTransaction => unreachable ! ( ) ,
1064+ FocusTransaction :: InTransaction ( ref elem) => {
1065+ elem. as_ref ( ) . map ( |e| DomRoot :: from_ref ( & * * e) )
1066+ } ,
1067+ } ;
1068+ * self . focus_transaction . borrow_mut ( ) = FocusTransaction :: NotInTransaction ;
1069+ if self . focused == possibly_focused. as_ref ( ) . map ( |e| & * * e) {
10291070 return ;
10301071 }
10311072 if let Some ( ref elem) = self . focused . get ( ) {
@@ -1040,7 +1081,7 @@ impl Document {
10401081 }
10411082 }
10421083
1043- self . focused . set ( self . possibly_focused . get ( ) . as_deref ( ) ) ;
1084+ self . focused . set ( possibly_focused. as_ref ( ) . map ( |e| & * * e ) ) ;
10441085
10451086 if let Some ( ref elem) = self . focused . get ( ) {
10461087 elem. set_focus_state ( true ) ;
@@ -1140,6 +1181,7 @@ impl Document {
11401181 }
11411182
11421183 self . begin_focus_transaction ( ) ;
1184+ self . request_focus ( Some ( & * el) , FocusType :: Element ) ;
11431185 }
11441186
11451187 // https://w3c.github.io/uievents/#event-type-click
@@ -2980,7 +3022,7 @@ impl Document {
29803022 stylesheet_list : MutNullableDom :: new ( None ) ,
29813023 ready_state : Cell :: new ( ready_state) ,
29823024 domcontentloaded_dispatched : Cell :: new ( domcontentloaded_dispatched) ,
2983- possibly_focused : Default :: default ( ) ,
3025+ focus_transaction : DomRefCell :: new ( FocusTransaction :: NotInTransaction ) ,
29843026 focused : Default :: default ( ) ,
29853027 current_script : Default :: default ( ) ,
29863028 pending_parsing_blocking_script : Default :: default ( ) ,
0 commit comments