@@ -169,50 +169,55 @@ const NAT64_DEFAULT = parseIP("64:ff9b::").slice(0, 96/4);
169169
170170let _watchOptionsFunc = null ;
171171const options = { ready : false , [ NAT64_KEY ] : new Set ( [ NAT64_DEFAULT ] ) } ;
172+ const optionsDirty = { } ; // {option: number of writes in flight}
172173const optionsReady = ( async function ( ) {
173- const items = await chrome . storage . sync . get ( ) ;
174- for ( const option of Object . keys ( DEFAULT_OPTIONS ) ) {
175- options [ option ] = items . hasOwnProperty ( option ) ?
176- items [ option ] : DEFAULT_OPTIONS [ option ] ;
174+ for ( const [ option , value ] of Object . entries ( DEFAULT_OPTIONS ) ) {
175+ options [ option ] = value ;
176+ optionsDirty [ option ] = 0 ;
177177 }
178- for ( const option of Object . keys ( items ) ) {
179- if ( NAT64_VALIDATE . test ( option ) ) {
178+ const items = await chrome . storage . sync . get ( ) ;
179+ for ( const [ option , value ] of Object . entries ( items ) ) {
180+ if ( DEFAULT_OPTIONS . hasOwnProperty ( option ) ) {
181+ options [ option ] = value ;
182+ } else if ( NAT64_VALIDATE . test ( option ) ) {
180183 options [ NAT64_KEY ] . add ( option . slice ( NAT64_KEY . length ) ) ;
181184 }
182185 }
183186 options . ready = true ;
184- if ( _watchOptionsFunc ) {
185- _watchOptionsFunc ( Object . keys ( options ) ) ;
186- }
187+ _watchOptionsFunc ?. ( Object . keys ( options ) ) ;
187188} ) ( ) ;
188189
189190chrome . storage . sync . onChanged . addListener ( function ( changes ) {
190191 // changes = {option: {oldValue: x, newValue: y}}
191192 if ( ! options . ready ) return ;
192193 const optionsChanged = [ ] ;
193- for ( const option of Object . keys ( DEFAULT_OPTIONS ) ) {
194- const change = changes [ option ] ;
195- if ( ! change ) continue ;
196- options [ option ] = change . hasOwnProperty ( "newValue" ) ?
197- change . newValue : DEFAULT_OPTIONS [ option ] ;
198- optionsChanged . push ( option ) ;
199- }
200- let nat64Changed = false ;
201194 for ( const [ option , { oldValue, newValue} ] of Object . entries ( changes ) ) {
202- if ( NAT64_VALIDATE . test ( option ) ) {
195+ if ( DEFAULT_OPTIONS . hasOwnProperty ( option ) ) {
196+ const value = newValue || DEFAULT_OPTIONS [ option ] ;
197+ if ( options [ option ] != value ) {
198+ if ( optionsDirty [ option ] > 1 ) {
199+ // Forget local changes that occurred mid-write.
200+ optionsDirty [ option ] = 1 ;
201+ }
202+ options [ option ] = value ;
203+ optionsChanged . push ( option ) ;
204+ }
205+ } else if ( NAT64_VALIDATE . test ( option ) ) {
203206 const packed96 = option . slice ( NAT64_KEY . length ) ;
204207 if ( newValue && ! options [ NAT64_KEY ] . has ( packed96 ) ) {
205208 options [ NAT64_KEY ] . add ( packed96 ) ;
206- nat64Changed = true ;
207209 } else if ( ! newValue && options [ NAT64_KEY ] . has ( packed96 ) ) {
208210 options [ NAT64_KEY ] . delete ( packed96 ) ;
209- nat64Changed = true ;
211+ } else {
212+ continue ; // no change
213+ }
214+ if ( ! optionsChanged . includes ( NAT64_KEY ) ) {
215+ optionsChanged . push ( NAT64_KEY ) ;
210216 }
211217 }
212218 }
213- if ( nat64Changed ) optionsChanged . push ( NAT64_KEY ) ;
214- if ( _watchOptionsFunc && optionsChanged . length ) {
215- _watchOptionsFunc ( optionsChanged ) ;
219+ if ( optionsChanged . length ) {
220+ _watchOptionsFunc ?. ( optionsChanged ) ;
216221 }
217222} ) ;
218223
@@ -225,18 +230,44 @@ function watchOptions(f) {
225230}
226231
227232function setOptions ( newOptions ) {
228- console . log ( "setOptions" , newOptions ) ;
229233 const toSet = { } ;
234+ const optionsChanged = [ ] ;
230235 for ( const option of Object . keys ( DEFAULT_OPTIONS ) ) {
231- if ( newOptions [ option ] != options [ option ] ) {
232- toSet [ option ] = newOptions [ option ] ;
236+ const value = newOptions [ option ] ;
237+ if ( options [ option ] != value ) {
238+ options [ option ] = value ;
239+ optionsChanged . push ( option ) ;
240+ if ( ++ optionsDirty [ option ] == 1 ) {
241+ toSet [ option ] = value ;
242+ } else {
243+ // dirty > 1; the value is buffered and written later.
244+ console . log ( "setOptions buffered" , option ) ;
245+ }
233246 }
234247 }
235- if ( Object . keys ( toSet ) . length == 0 ) {
236- return false ; // no change
248+ const doSet = ( ) => {
249+ if ( Object . keys ( toSet ) . length == 0 ) {
250+ return ; // no change
251+ }
252+ chrome . storage . sync . set ( toSet , ( ) => {
253+ for ( const [ option , value ] of Object . entries ( toSet ) ) {
254+ const dirty = optionsDirty [ option ] ;
255+ if ( dirty > 1 && value != options [ option ] ) {
256+ // user changed the value mid-write; push the latest value.
257+ toSet [ option ] = options [ option ] ;
258+ optionsDirty [ option ] = 1 ;
259+ } else {
260+ delete toSet [ option ] ;
261+ optionsDirty [ option ] = 0 ;
262+ }
263+ }
264+ doSet ( ) ;
265+ } ) ;
266+ } ;
267+ doSet ( ) ;
268+ if ( optionsChanged . length ) {
269+ _watchOptionsFunc ?. ( optionsChanged ) ;
237270 }
238- chrome . storage . sync . set ( toSet ) ;
239- return true ; // caller should wait for watchOptions()
240271}
241272
242273// Users can manually call this function to add a NAT64 prefix from the console.
@@ -287,4 +318,4 @@ if (chrome.runtime.getManifest().background.service_worker &&
287318 query . addEventListener ( "change" , ( event ) => {
288319 chrome . runtime . sendMessage ( { darkModeInteractive : event . matches } ) ;
289320 } ) ;
290- }
321+ }
0 commit comments