@@ -45,7 +45,6 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
4545 const makeSystem = ( origin , name , labelElementId , switchElementId , messageType ) => Object . freeze ( {
4646 origin,
4747 name,
48- title : ProtectionResult . FullName [ origin ] ,
4948 labelElementId,
5049 switchElementId,
5150 messageType,
@@ -70,9 +69,6 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
7069 makeSystem ( ProtectionResult . Origin . SWITCH_CH , "switchCHEnabled" , "switchCHStatus" , "switchCHSwitch" , Messages . SWITCH_CH_TOGGLED ) ,
7170 ] ) ;
7271
73- // Cached manifest data
74- const manifest = browserAPI . runtime . getManifest ( ) ;
75-
7672 /**
7773 * Gets DOM elements for a system, caching them for future use.
7874 *
@@ -109,40 +105,29 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
109105 return ;
110106 }
111107
112- const updates = [ ] ;
113-
114- updates . push ( ( ) => {
115- if ( elements . label ) {
116- if ( isLocked ) {
117- elements . label . textContent = isOn ? LangUtil . ON_LOCKED_TEXT : LangUtil . OFF_LOCKED_TEXT ;
118- } else {
119- elements . label . textContent = isOn ? LangUtil . ON_TEXT : LangUtil . OFF_TEXT ;
120- }
108+ if ( elements . label ) {
109+ if ( isLocked ) {
110+ elements . label . textContent = isOn ? LangUtil . ON_LOCKED_TEXT : LangUtil . OFF_LOCKED_TEXT ;
121111 } else {
122- console . warn ( `' label' element not found for ${ system . name } in the PopupPage DOM.` ) ;
112+ elements . label . textContent = isOn ? LangUtil . ON_TEXT : LangUtil . OFF_TEXT ;
123113 }
114+ } else {
115+ console . warn ( `'label' element not found for ${ system . name } in the PopupPage DOM.` ) ;
116+ }
124117
125- if ( elements . switchElement ) {
126- if ( isOn ) {
127- elements . switchElement . classList . add ( "on" ) ;
128- elements . switchElement . classList . remove ( "off" ) ;
129- elements . switchElement . setAttribute ( "aria-checked" , "true" ) ;
130- } else {
131- elements . switchElement . classList . remove ( "on" ) ;
132- elements . switchElement . classList . add ( "off" ) ;
133- elements . switchElement . setAttribute ( "aria-checked" , "false" ) ;
134- }
118+ if ( elements . switchElement ) {
119+ if ( isOn ) {
120+ elements . switchElement . classList . add ( "on" ) ;
121+ elements . switchElement . classList . remove ( "off" ) ;
122+ elements . switchElement . setAttribute ( "aria-checked" , "true" ) ;
135123 } else {
136- console . warn ( `'switchElement' not found for ${ system . name } in the PopupPage DOM.` ) ;
137- }
138- } ) ;
139-
140- // Batches the DOM updates for performance
141- globalThis . requestAnimationFrame ( ( ) => {
142- for ( const update of updates ) {
143- update ( ) ;
124+ elements . switchElement . classList . remove ( "on" ) ;
125+ elements . switchElement . classList . add ( "off" ) ;
126+ elements . switchElement . setAttribute ( "aria-checked" , "false" ) ;
144127 }
145- } ) ;
128+ } else {
129+ console . warn ( `'switchElement' not found for ${ system . name } in the PopupPage DOM.` ) ;
130+ }
146131 } ;
147132
148133 /**
@@ -152,18 +137,34 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
152137 */
153138 const toggleProtection = system => {
154139 Settings . get ( settings => {
155- const currentState = settings [ system . name ] ;
156- const newState = ! currentState ;
140+ if ( ! settings || typeof settings !== 'object' ) {
141+ console . error ( `PopupPage: Settings.get returned invalid settings in toggleProtection for ${ system . name } ; aborting toggle.` ) ;
142+ return ;
143+ }
144+
145+ const newState = ! settings [ system . name ] ;
157146
158147 Settings . set ( { [ system . name ] : newState } , ( ) => {
159- updateProtectionStatusUI ( system , newState , settings . lockProtectionOptions ) ;
160-
161- browserAPI . runtime . sendMessage ( {
162- messageType : system . messageType ,
163- title : ProtectionResult . FullName [ system . origin ] ,
164- toggleState : newState ,
165- } ) . catch ( error => {
166- console . error ( `Failed to send message for ${ system . name } :` , error ) ;
148+ Settings . get ( verified => {
149+ if ( ! verified || typeof verified !== 'object' ) {
150+ console . error ( `PopupPage: Could not verify settings write for ${ system . name } ; aborting UI update.` ) ;
151+ return ;
152+ }
153+
154+ if ( verified [ system . name ] !== newState ) {
155+ console . error ( `PopupPage: Settings write verification failed for ${ system . name } ; expected ${ newState } , got ${ verified [ system . name ] } .` ) ;
156+ return ;
157+ }
158+
159+ updateProtectionStatusUI ( system , newState , verified . lockProtectionOptions ) ;
160+
161+ browserAPI . runtime . sendMessage ( {
162+ messageType : system . messageType ,
163+ title : ProtectionResult . FullName [ system . origin ] ,
164+ toggleState : newState ,
165+ } ) . catch ( error => {
166+ console . error ( `Failed to send message for ${ system . name } :` , error ) ;
167+ } ) ;
167168 } ) ;
168169 } ) ;
169170 } ) ;
@@ -201,17 +202,17 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
201202 * Initializes the popup or refresh if already initialized.
202203 */
203204 const initialize = ( ) => {
205+ // If already initialized, reset first
206+ if ( isInitialized ) {
207+ reset ( ) ;
208+ }
209+
204210 // Initializes the DOM element cache
205211 domElements = Object . fromEntries (
206212 [ "popupTitle" , "githubLink" , "version" , "privacyPolicy" , "logo" , "prevPage" , "nextPage" , "pageIndicator" ]
207213 . map ( id => [ id , document . getElementById ( id ) ] )
208214 ) ;
209215
210- // If already initialized, reset first
211- if ( isInitialized ) {
212- reset ( ) ;
213- }
214-
215216 // Marks initialized as true
216217 isInitialized = true ;
217218
@@ -222,11 +223,7 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
222223 const bannerText = document . querySelector ( '.bannerText' ) ;
223224
224225 // Sets the document title text
225- if ( document . title ) {
226- document . title = LangUtil . TITLE ;
227- } else {
228- console . warn ( "Document title not found in the PopupPage DOM." ) ;
229- }
226+ document . title = LangUtil . TITLE ;
230227
231228 // Sets the banner text
232229 if ( bannerText ) {
@@ -236,9 +233,10 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
236233 }
237234
238235 // Sets titles and aria-labels for star symbols and partner labels
236+ const officialPartnerTitle = LangUtil . OFFICIAL_PARTNER_TITLE ;
239237 for ( const element of document . querySelectorAll ( '.starSymbol, .partnerLabel' ) ) {
240- element . setAttribute ( 'title' , LangUtil . OFFICIAL_PARTNER_TITLE ) ;
241- element . setAttribute ( 'aria-label' , LangUtil . OFFICIAL_PARTNER_TITLE ) ;
238+ element . setAttribute ( 'title' , officialPartnerTitle ) ;
239+ element . setAttribute ( 'aria-label' , officialPartnerTitle ) ;
242240 }
243241
244242 // Sets the alt text for the Osprey logo
@@ -262,13 +260,6 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
262260 console . warn ( "'githubLink' element not found in the PopupPage DOM." ) ;
263261 }
264262
265- // Sets the version text
266- if ( domElements . version ) {
267- domElements . version . textContent = LangUtil . VERSION ;
268- } else {
269- console . warn ( "'version' element not found in the PopupPage DOM." ) ;
270- }
271-
272263 // Sets the Privacy Policy text
273264 if ( domElements . privacyPolicy ) {
274265 domElements . privacyPolicy . textContent = LangUtil . PRIVACY_POLICY ;
@@ -291,6 +282,11 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
291282 if ( elements . switchElement ) {
292283 elements . switchElement . onclick = ( ) => {
293284 Settings . get ( settings => {
285+ if ( ! settings || typeof settings !== 'object' ) {
286+ console . error ( "PopupPage: Settings.get returned invalid settings in switch handler; aborting toggle." ) ;
287+ return ;
288+ }
289+
294290 if ( settings . lockProtectionOptions ) {
295291 console . debug ( "Protections are locked; cannot toggle." ) ;
296292 } else {
@@ -302,7 +298,7 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
302298 elements . switchElement . onkeydown = e => {
303299 if ( e . key === " " || e . key === "Enter" ) {
304300 e . preventDefault ( ) ;
305- elements . switchElement . onclick ( ) ;
301+ elements . switchElement . click ( ) ;
306302 }
307303 } ;
308304 } else {
@@ -319,8 +315,7 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
319315
320316 // Updates the version display
321317 if ( domElements . version ) {
322- const { version} = manifest ;
323- domElements . version . textContent = LangUtil . VERSION + version ;
318+ domElements . version . textContent = LangUtil . VERSION + browserAPI . runtime . getManifest ( ) . version ;
324319 }
325320
326321 // Get all elements with the class 'page'
@@ -334,35 +329,32 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
334329 return ;
335330 }
336331
337- const updatePageDisplay = ( ) => {
338- // Checks for invalid current page numbers
339- if ( currentPage < 1 || currentPage > totalPages ) {
340- currentPage = 1 ;
332+ const updatePageDisplay = ( newPage ) => {
333+ if ( newPage < 1 || newPage > totalPages ) {
334+ return ;
341335 }
342336
343- // Toggles the active status
344- for ( let i = 0 ; i < pages . length ; i ++ ) {
345- pages [ i ] . classList . toggle ( 'active' , i + 1 === currentPage ) ;
346- }
337+ pages [ currentPage - 1 ] . classList . remove ( 'active' ) ;
338+ currentPage = newPage ;
339+ pages [ currentPage - 1 ] . classList . add ( 'active' ) ;
347340
348- // Updates the page indicator
349341 if ( domElements . pageIndicator ) {
350342 domElements . pageIndicator . textContent = `${ currentPage } /${ totalPages } ` ;
343+ domElements . pageIndicator . setAttribute ( 'aria-label' , `${ LangUtil . PAGE_INDICATOR_LABEL } ${ currentPage } ${ LangUtil . PAGE_INDICATOR_OF } ${ totalPages } ` ) ;
351344 } else {
352345 console . warn ( "'pageIndicator' element not found in the PopupPage DOM." ) ;
353346 }
354347 } ;
355348
356349 if ( domElements . prevPage ) {
357350 domElements . prevPage . onclick = ( ) => {
358- currentPage = currentPage === 1 ? totalPages : currentPage - 1 ;
359- updatePageDisplay ( ) ;
351+ updatePageDisplay ( currentPage === 1 ? totalPages : currentPage - 1 ) ;
360352 } ;
361353
362354 domElements . prevPage . onkeydown = e => {
363355 if ( e . key === " " || e . key === "Enter" ) {
364356 e . preventDefault ( ) ;
365- domElements . prevPage . onclick ( ) ;
357+ domElements . prevPage . click ( ) ;
366358 }
367359 } ;
368360 } else {
@@ -371,22 +363,21 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
371363
372364 if ( domElements . nextPage ) {
373365 domElements . nextPage . onclick = ( ) => {
374- currentPage = currentPage === totalPages ? 1 : currentPage + 1 ;
375- updatePageDisplay ( ) ;
366+ updatePageDisplay ( currentPage === totalPages ? 1 : currentPage + 1 ) ;
376367 } ;
377368
378369 domElements . nextPage . onkeydown = e => {
379370 if ( e . key === " " || e . key === "Enter" ) {
380371 e . preventDefault ( ) ;
381- domElements . nextPage . onclick ( ) ;
372+ domElements . nextPage . click ( ) ;
382373 }
383374 } ;
384375 } else {
385376 console . warn ( "'nextPage' element not found in the PopupPage DOM." ) ;
386377 }
387378
388379 // Initializes the page display
389- updatePageDisplay ( ) ;
380+ updatePageDisplay ( 1 ) ;
390381 } ;
391382
392383 return Object . freeze ( {
@@ -398,7 +389,7 @@ globalThis.PopupSingleton = globalThis.PopupSingleton || (() => {
398389document . addEventListener ( "DOMContentLoaded" , ( ) => {
399390 try {
400391 Settings . get ( settings => {
401- if ( settings . hideProtectionOptions ) {
392+ if ( ! settings || typeof settings !== 'object' || settings . hideProtectionOptions ) {
402393 globalThis . close ( ) ;
403394 } else {
404395 globalThis . PopupSingleton . initialize ( ) ;
0 commit comments