@@ -24,6 +24,7 @@ const { FIELD_STATES } = FormAutofillUtils;
24
24
export const FORM_CHANGE_REASON = {
25
25
NODES_ADDED : "nodes-added" ,
26
26
NODES_REMOVED : "nodes-removed" ,
27
+ SELECT_OPTIONS_CHANGED : "select-options-changed" ,
27
28
ELEMENT_INVISIBLE : "visible-element-became-invisible" ,
28
29
ELEMENT_VISIBLE : "invisible-element-became-visible" ,
29
30
} ;
@@ -268,6 +269,11 @@ export class FormAutofillHandler {
268
269
return false ;
269
270
}
270
271
272
+ updateFormByElement ( element ) {
273
+ const formLike = lazy . AutofillFormFactory . createFromField ( element ) ;
274
+ this . _updateForm ( formLike ) ;
275
+ }
276
+
271
277
/**
272
278
* Update the form with a new FormLike, and the related fields should be
273
279
* updated or clear to ensure the data consistency.
@@ -344,7 +350,7 @@ export class FormAutofillHandler {
344
350
}
345
351
346
352
/**
347
- * Resetting the state element's fieldDetail after it was removed from the form
353
+ * Resetting the filled state after an element was removed from the form
348
354
* Todo: We'll need to update this.filledResult in FormAutofillParent (Bug 1948077).
349
355
*
350
356
* @param {HTMLElement } element that was removed
@@ -353,8 +359,7 @@ export class FormAutofillHandler {
353
359
if ( this . getFilledStateByElement ( element ) != FIELD_STATES . AUTO_FILLED ) {
354
360
return ;
355
361
}
356
- const fieldDetail = this . getFieldDetailByElement ( element ) ;
357
- this . #filledStateByElement. delete ( fieldDetail ) ;
362
+ this . #filledStateByElement. delete ( element ) ;
358
363
}
359
364
360
365
/**
@@ -493,6 +498,14 @@ export class FormAutofillHandler {
493
498
} else if ( HTMLSelectElement . isInstance ( element ) ) {
494
499
const option = this . matchSelectOptions ( fieldDetail , profile ) ;
495
500
if ( ! option ) {
501
+ if (
502
+ this . getFilledStateByElement ( element ) == FIELD_STATES . AUTO_FILLED
503
+ ) {
504
+ // The select element was previously autofilled, but there
505
+ // is no matching option under the current set of options anymore.
506
+ // Changing the state will also remove the highlighting from the element
507
+ this . changeFieldState ( fieldDetail , FIELD_STATES . NORMAL ) ;
508
+ }
496
509
continue ;
497
510
}
498
511
@@ -684,18 +697,21 @@ export class FormAutofillHandler {
684
697
const mutationObserver = new this . window . MutationObserver (
685
698
( mutations , _ ) => {
686
699
const collectMutatedNodes = mutations => {
687
- let removedNodes = [ ] ;
688
- let addedNodes = [ ] ;
700
+ let removedNodes = new Set ( ) ;
701
+ let addedNodes = new Set ( ) ;
702
+ let changedSelectElements = new Set ( ) ;
689
703
mutations . forEach ( mutation => {
690
704
if ( mutation . type == "childList" ) {
691
- if ( mutation . addedNodes . length ) {
692
- addedNodes . push ( ...mutation . addedNodes ) ;
705
+ if ( HTMLSelectElement . isInstance ( mutation . target ) ) {
706
+ changedSelectElements . add ( mutation . target ) ;
707
+ } else if ( mutation . addedNodes . length ) {
708
+ addedNodes . add ( ...mutation . addedNodes ) ;
693
709
} else if ( mutation . removedNodes . length ) {
694
- removedNodes . push ( ...mutation . removedNodes ) ;
710
+ removedNodes . add ( ...mutation . removedNodes ) ;
695
711
}
696
712
}
697
713
} ) ;
698
- return [ addedNodes , removedNodes ] ;
714
+ return [ addedNodes , removedNodes , changedSelectElements ] ;
699
715
} ;
700
716
701
717
const collectAllSubtreeElements = node => {
@@ -715,22 +731,35 @@ export class FormAutofillHandler {
715
731
) ;
716
732
} ;
717
733
718
- let [ addedNodes , removedNodes ] = collectMutatedNodes ( mutations ) ;
719
- let relevantAddedElements = getCCAndAddressElements ( addedNodes ) ;
720
- // We only care about removed elements that might change the
721
- // currently detected fieldDetails
722
- let relevantRemovedElements = getCCAndAddressElements (
723
- removedNodes
724
- ) . filter (
734
+ const [ addedNodes , removedNodes , changedSelectElements ] =
735
+ collectMutatedNodes ( mutations ) ;
736
+ let relevantAddedElements = getCCAndAddressElements ( [ ...addedNodes ] ) ;
737
+ // We only care about removed elements and changed select options
738
+ // from the current set of detected fieldDetails
739
+ let relevantRemovedElements = getCCAndAddressElements ( [
740
+ ...removedNodes ,
741
+ ] ) . filter (
742
+ element =>
743
+ this . #fieldDetails && ! ! this . getFieldDetailByElement ( element )
744
+ ) ;
745
+ let relevantChangedSelectElements = [ ...changedSelectElements ] . filter (
725
746
element =>
726
747
this . #fieldDetails && ! ! this . getFieldDetailByElement ( element )
727
748
) ;
728
749
729
- if ( ! relevantRemovedElements . length && ! relevantAddedElements . length ) {
750
+ if (
751
+ ! relevantRemovedElements . length &&
752
+ ! relevantAddedElements . length &&
753
+ ! relevantChangedSelectElements . length
754
+ ) {
730
755
return ;
731
756
}
732
757
733
758
let changes = { } ;
759
+ if ( relevantChangedSelectElements . length ) {
760
+ changes [ FORM_CHANGE_REASON . SELECT_OPTIONS_CHANGED ] =
761
+ relevantChangedSelectElements ;
762
+ }
734
763
if ( relevantRemovedElements . length ) {
735
764
changes [ FORM_CHANGE_REASON . NODES_REMOVED ] = relevantRemovedElements ;
736
765
}
@@ -875,7 +904,8 @@ export class FormAutofillHandler {
875
904
const value = profile [ fieldName ] ;
876
905
877
906
let option = cache [ value ] ?. deref ( ) ;
878
- if ( ! option ) {
907
+
908
+ if ( ! option || ! option . isConnected ) {
879
909
option = FormAutofillUtils . findSelectOption ( element , profile , fieldName ) ;
880
910
881
911
if ( option ) {
0 commit comments