11import { augmentPageWithWordPressThemeStyles } from './wordpress'
2- import { buildPopupCacheRecord , cleanPageDetectionRecord } from './popup-cache'
2+ import { buildPopupCacheRecord , cleanPageDetectionRecord , mergePageDetectionRecord } from './popup-cache'
33import { fetchMainHeadersFallback , mergeHeaderRecords } from './headers'
44import { clearBadge , clearTabSession , getTabData , getTabSnapshot , updateBadgeForTab , writeTabData } from './tab-store'
55import { buildEffectivePageRules , loadDetectorSettings , loadTechRules } from './detector-settings'
66import { scheduleBundleLicenseDetection } from './bundle-license'
77import { injectContentObserver } from './content-injector'
8+ import { withTabWriteLock } from './tab-write-lock'
89import { isDetectablePageUrl } from '@/utils/page-support'
910
1011const activeDetectionTimers = new Map < number , ReturnType < typeof setTimeout > > ( )
@@ -106,7 +107,9 @@ export const runActivePageDetection = async (tabId: number, options: { force?: b
106107 lastDetectionRunAt . set ( tabId , Date . now ( ) )
107108 console . log ( '[SP detection] run start' , tabId , 'force:' , Boolean ( options . force ) )
108109 await injectContentObserver ( tabId )
109- const [ data , rules , settings ] = await Promise . all ( [ getTabData ( tabId ) , loadTechRules ( ) , loadDetectorSettings ( ) ] )
110+ // 这里不再预读 data —— page-detector 注入要 500ms+,期间其他 writer 会写过 storage;
111+ // 等 detector 跑完再统一 re-read 最新 data 再做合并写回
112+ const [ rules , settings ] = await Promise . all ( [ loadTechRules ( ) , loadDetectorSettings ( ) ] )
110113 const pageRules = buildEffectivePageRules ( rules . page || { } , settings )
111114 await chrome . scripting . executeScript ( {
112115 target : { tabId } ,
@@ -125,21 +128,36 @@ export const runActivePageDetection = async (tabId: number, options: { force?: b
125128 if ( ! page ) return
126129
127130 const augmentedPage = await augmentPageWithWordPressThemeStyles ( page )
128- data . page = cleanPageDetectionRecord ( augmentedPage )
129-
130- if ( needsMainHeadersFallback ( data . main , ( page as any ) . url || tab . url ) ) {
131- const fallback = await fetchMainHeadersFallback ( ( page as any ) . url || '' , rules . headers || { } , settings )
132- if ( fallback ) {
133- data . main = shouldPreserveMainHeaderRecord ( data . main , ( page as any ) . url || tab . url )
134- ? mergeHeaderRecords ( data . main , fallback )
135- : fallback
136- } else if ( data . main && ! shouldPreserveMainHeaderRecord ( data . main , ( page as any ) . url || tab . url ) ) {
137- delete data . main
138- }
131+ const freshClean = cleanPageDetectionRecord ( augmentedPage )
132+
133+ // 进 per-tab 锁:read-modify-write 段必须跟其他 writer 串行,避免 dynamic/bundle/headers 并发读到同一份旧快照,
134+ // 互相把对方刚写好的字段覆盖掉(popup 上数字回落 )
135+ let fallbackForMain : any = null
136+ const needsFallback = await withTabWriteLock ( tabId , async ( ) => {
137+ const peek = ( await getTabData ( tabId ) ) || { }
138+ return needsMainHeadersFallback ( peek . main , ( page as any ) . url || tab . url )
139+ } )
140+ if ( needsFallback ) {
141+ fallbackForMain = await fetchMainHeadersFallback ( ( page as any ) . url || '' , rules . headers || { } , settings )
139142 }
140143
141- data . updatedAt = Date . now ( )
142- await saveTabDataAndBadge ( tabId , data , settings )
144+ await withTabWriteLock ( tabId , async ( ) => {
145+ const latest = ( await getTabData ( tabId ) ) || { }
146+ latest . page = mergePageDetectionRecord ( latest . page , freshClean )
147+
148+ if ( needsMainHeadersFallback ( latest . main , ( page as any ) . url || tab . url ) ) {
149+ if ( fallbackForMain ) {
150+ latest . main = shouldPreserveMainHeaderRecord ( latest . main , ( page as any ) . url || tab . url )
151+ ? mergeHeaderRecords ( latest . main , fallbackForMain )
152+ : fallbackForMain
153+ } else if ( latest . main && ! shouldPreserveMainHeaderRecord ( latest . main , ( page as any ) . url || tab . url ) ) {
154+ delete latest . main
155+ }
156+ }
157+
158+ latest . updatedAt = Date . now ( )
159+ await saveTabDataAndBadge ( tabId , latest , settings )
160+ } )
143161 scheduleBundleLicenseDetection ( tabId )
144162 } catch {
145163 return
0 commit comments