import { browser } from './shared/globals' ; import { Config , Events } from './shared/types' ; const configs = new Map < number , Config > ( ) ; const readyTabs = new Set < number > ( ) ; const queuedEvents = new Map < number , Events [ ] > ( ) ; const devtoolsPorts = new Map < number , chrome . runtime . Port > ( ) ; /** Emit an event to a tab's content script if ready; queue otherwise. */ const sendEvent = ( tabId , event ) => { if ( readyTabs . has ( tabId ) ) { browser . tabs . sendMessage ( tabId , event ) ; } else { if ( ! queuedEvents . has ( tabId ) ) { queuedEvents . set ( tabId , [ ] ) ; } const events = queuedEvents . get ( tabId ) ! ; events . push ( event ) ; } } ; /** Add the script to run webhint to the page. */ const injectContentScript = ( tabId , retries = 0 ) => { browser . tabs . executeScript ( tabId , { file : 'content-script/webhint.js' , runAt : 'document_start' } , ( result ) => { if ( ! result ) { if ( retries <= 2 ) { console . warn ( 'Failed to inject content script. Retrying...' ) ; injectContentScript ( tabId , retries + 1 ) ; } else { const port = devtoolsPorts . get ( tabId ) ; const message = 'Failed to inject content script after retrying.' ; const event = { error : { message , stack : '' } } ; console . error ( message ) ; if ( port ) { port . postMessage ( event ) ; } } } } ) ; } ; /** Turn on request tracking for the specified tab. */ const enable = ( tabId ) => { readyTabs . delete ( tabId ) ; let timeout = 0 ; const onCommitted = ( details ) => { if ( details . tabId === tabId && details . frameId === 0 ) { browser . webNavigation . onCommitted . removeListener ( onCommitted ) ; clearTimeout ( timeout ) ; injectContentScript ( tabId ) ; } } ; browser . webNavigation . onCommitted . addListener ( onCommitted ) ; timeout = setTimeout ( ( ) => { browser . webNavigation . onCommitted . removeListener ( onCommitted ) ; } , 10000 ) as any ; browser . tabs . reload ( tabId , { bypassCache : true } ) ; } ; browser . runtime . onConnect . addListener ( ( port ) => { devtoolsPorts . set ( parseInt ( port . name ) , port ) ; } ) ; browser . runtime . onMessage . addListener ( ( message , sender ) => { const tabId = message . tabId || sender . tab && sender . tab . id ; if ( ! tabId ) { throw new Error ( `Message received without a tabId: ${ JSON . stringify ( message ) }` ) ; } if ( message . enable ) { configs . set ( tabId , message . enable . config ) ; enable ( tabId ) ; } if ( message . evaluateResult ) { browser . tabs . sendMessage ( tabId , message ) ; } if ( message . requestConfig ) { const configMessage = { config : configs . get ( tabId ) || { } } ; browser . tabs . sendMessage ( tabId , configMessage ) ; } if ( message . ready ) { readyTabs . add ( tabId ) ; if ( queuedEvents . has ( tabId ) ) { const events = queuedEvents . get ( tabId ) ! ; events . forEach ( ( event ) => { sendEvent ( tabId , event ) ; } ) ; queuedEvents . delete ( tabId ) ; } } if ( message . fetchEnd || message . fetchStart ) { sendEvent ( tabId , message ) ; } if ( message . error || message . evaluate || message . results ) { const port = devtoolsPorts . get ( tabId ) ; if ( port ) { port . postMessage ( message ) ; } } } ) ;