2121"use strict" ;
2222var Storage = ( ( ) => {
2323
24- let chunksKey = k => `${ k } /CHUNKS` ;
24+ const chunksKey = k => `${ k } /CHUNKS` ;
25+
26+ let lazyInitSync = async ( ) => {
27+ lazyInitSync = null ;
28+
29+ const SYNC_KEYS = "__ALL_SYNC_KEYS__" ;
30+ let allSyncData ;
31+ try {
32+ allSyncData = await browser . storage . sync . get ( ) ;
33+ } catch ( e ) {
34+ // sync storage is disabled, bail out
35+ const syncKeys = ( await browser . storage . local . get ( SYNC_KEYS ) ) [ SYNC_KEYS ] ;
36+ if ( syncKeys ) {
37+ const fallbackKeys = await getLocalFallback ( ) ;
38+ await setLocalFallback ( new Set ( [ ...fallbackKeys ] . concat ( syncKeys ) ) )
39+ }
40+ return ;
41+ }
42+
43+ const chunkedRx = / ^ ( [ ^ / ] + ) \/ ( \d + | C H U N K S ) $ / ;
44+ let keys = Object . keys ( allSyncData ) ;
45+
46+ // sanitize / repair chunked keys
47+ const safeKeys = [ ] ;
48+ const repaired = { } ;
49+
50+ for ( let k of keys ) {
51+ if ( ! k . endsWith ( "/0" ) ) continue ;
52+ const [ keyName ] = k . split ( "/" ) ;
53+ if ( allSyncData [ keyName ] !== "[CHUNKED]" ) {
54+ // not flagged as chunked, bail out and doome the remnants
55+ continue ;
56+ }
57+ const ccKey = chunksKey ( keyName ) ;
58+ let count = parseInt ( allSyncData [ ccKey ] ) ;
59+ let contiguousKeys = [ ] ;
60+ const keyPrefix = keyName . concat ( '/' ) ;
61+ for ( let j = 1 ; ; j ++ ) {
62+ contiguousKeys . push ( k ) ;
63+ if ( j >= count ) break ;
64+ k = keyPrefix . concat ( j ) ;
65+ if ( ! keys . includes ( k ) ) break ;
66+ }
67+ let actualCount = contiguousKeys . length ;
68+ if ( count === actualCount ) continue ;
69+ // try to repair
70+ repaired [ ccKey ] = actualCount ;
71+ safeKeys . push ( ccKey , ...contiguousKeys ) ;
72+ }
73+
74+ const doomedKeys = keys . filter ( k => chunkedRx . test ( k ) && ! safeKeys . includes ( k ) ) ;
75+ if ( doomedKeys . length ) {
76+ await browser . storage . sync . remove ( doomedKeys ) ;
77+ }
78+ if ( Object . keys ( repaired ) . length ) {
79+ await browser . storage . sync . set ( repaired ) ;
80+ }
81+
82+ {
83+ // backup sync data on local to survive on the fly sync disablement
84+ const localKeys = Object . keys ( await browser . storage . local . get ( ) ) ;
85+ const syncKeys = keys . filter ( k => ! chunkedRx . test ( k ) ) ;
86+ const backupKeys = syncKeys . filter ( k => ! localKeys . includes ( k ) ) ;
87+ const backupData = await Storage . get ( "sync" , backupKeys ) ;
88+ backupData [ SYNC_KEYS ] = syncKeys ;
89+ await browser . storage . local . set ( backupData ) ;
90+ }
91+
92+
93+ } ;
2594
2695 async function safeOp ( op , type , keys ) {
2796 let sync = type === "sync" ;
28-
2997 try {
3098 if ( sync ) {
99+
100+ if ( lazyInitSync ) await lazyInitSync ( ) ;
101+
31102 let remove = op === "remove" ;
32103 if ( remove || op === "get" ) {
33104 keys = [ ] . concat ( keys ) ; // don't touch the passed argument
105+ if ( remove ) {
106+ // remove local backup
107+ await browser . storage . local . remove ( keys ) ;
108+ }
34109 let mergeResults = { } ;
35110 let localFallback = await getLocalFallback ( ) ;
36111 if ( localFallback . size ) {
37112 let localKeys = keys . filter ( k => localFallback . has ( k ) ) ;
38113 if ( localKeys . length ) {
39114 if ( remove ) {
40- await browser . storage . local . remove ( localKeys ) ;
41115 for ( let k of localKeys ) {
42116 localFallback . delete ( k ) ;
43117 }
44118 await setLocalFallback ( localFallback ) ;
45119 } else {
46120 mergeResults = await browser . storage . local . get ( localKeys ) ;
121+ keys = keys . filter ( k => ! localFallback . has ( k ) ) ;
47122 }
48- keys = keys . filter ( k => ! localFallback . has ( k ) ) ;
49123 }
50124 }
51125
52- if ( keys . length ) { // we may not have non-fallback keys anymore
126+ if ( keys . length ) { // we may not have non-fallback keys anymore (for get)
53127 let chunkCounts = Object . entries ( await browser . storage . sync . get (
54128 keys . map ( chunksKey ) ) )
55129 . map ( ( [ k , count ] ) => [ k . split ( "/" ) [ 0 ] , count ] ) ;
@@ -60,9 +134,10 @@ var Storage = (() => {
60134 while ( count -- > 0 ) chunkedKeys . push ( `${ k } /${ count } ` ) ;
61135 }
62136 if ( remove ) {
63- let doomedKeys = keys
137+ const doomedKeys = keys
64138 . concat ( chunkCounts . map ( ( [ k , count ] ) => chunksKey ( k ) ) )
65139 . concat ( chunkedKeys ) ;
140+ // remove all the keys included chunked, if any, from sync storage
66141 return await browser . storage . sync . remove ( doomedKeys ) ;
67142 } else {
68143 let chunks = await browser . storage . sync . get ( chunkedKeys ) ;
@@ -86,6 +161,9 @@ var Storage = (() => {
86161 Object . assign ( mergeResults , await browser . storage . sync [ op ] ( keys ) )
87162 : mergeResults ;
88163 } else if ( op === "set" ) {
164+ // create/update local backup
165+ await browser . storage . local . set ( keys ) ;
166+
89167 keys = Object . assign ( { } , keys ) ; // don't touch the passed argument
90168 const MAX_ITEM_SIZE = 4096 ;
91169 // Firefox Sync's max object BYTEs size is 16384, Chrome's 8192.
@@ -134,17 +212,17 @@ var Storage = (() => {
134212 }
135213 return ret ;
136214 } catch ( e ) {
137- error ( e , "%s.%s(%o)" , type , op , keys ) ;
138215 if ( sync ) {
139- debug ( "Sync disabled? Falling back to local storage (%s %o) " , op , keys ) ;
140- let localFallback = await getLocalFallback ( ) ;
141- let failedKeys = Array . isArray ( keys ) ? keys
216+ debug ( "Sync disabled? Falling back to local storage" , op , keys , e ) ;
217+ const localFallback = await getLocalFallback ( ) ;
218+ const failedKeys = Array . isArray ( keys ) ? keys
142219 : typeof keys === "string" ? [ keys ] : Object . keys ( keys ) ;
143220 for ( let k of failedKeys ) {
144221 localFallback . add ( k ) ;
145222 }
146223 await setLocalFallback ( localFallback ) ;
147224 } else {
225+ error ( e , "%s.%s(%o)" , type , op , keys ) ;
148226 throw e ;
149227 }
150228 }
@@ -161,6 +239,12 @@ var Storage = (() => {
161239 return new Set ( Array . isArray ( keys ) ? keys : [ ] ) ;
162240 }
163241
242+ async function isChunked ( key ) {
243+ let ccKey = chunksKey ( key ) ;
244+ let data = await browser . storage . sync . get ( [ key , ccKey ] ) ;
245+ return data [ key ] === "[CHUNKED]" && parseInt ( data [ ccKey ] ) ;
246+ }
247+
164248 return {
165249 async get ( type , keys ) {
166250 return await safeOp ( "get" , type , keys ) ;
@@ -178,10 +262,6 @@ var Storage = (() => {
178262 return ( await getLocalFallback ( ) ) . has ( key ) ;
179263 } ,
180264
181- async isChunked ( key ) {
182- let ccKey = chunksKey ( key ) ;
183- let data = await browser . storage . sync . get ( [ key , ccKey ] ) ;
184- return data [ key ] === "[CHUNKED]" && parseInt ( data [ ccKey ] ) ;
185- }
265+ isChunked,
186266 } ;
187267} ) ( )
0 commit comments