/* global chrome */ class CouldNotFindReactOnThePageError extends Error { constructor() { super("Could not find React, or it hasn't been loaded yet"); // Maintains proper stack trace for where our error was thrown (only available on V8) if (Error.captureStackTrace) { Error.captureStackTrace(this, CouldNotFindReactOnThePageError); } this.name = 'CouldNotFindReactOnThePageError'; } } export function startReactPolling( onReactFound, attemptsThreshold, onCouldNotFindReactAfterReachingAttemptsThreshold, ) { let status = 'idle'; function abort() { status = 'aborted'; } // This function will call onSuccess only if React was found and polling is not aborted, onError will be called for every other case function checkIfReactPresentInInspectedWindow(onSuccess, onError) { chrome.devtools.inspectedWindow.eval( 'window.__REACT_DEVTOOLS_GLOBAL_HOOK__ && window.__REACT_DEVTOOLS_GLOBAL_HOOK__.renderers.size > 0', (pageHasReact, exceptionInfo) => { if (status === 'aborted') { onError( 'Polling was aborted, user probably navigated to the other page', ); return; } if (exceptionInfo) { const {code, description, isError, isException, value} = exceptionInfo; if (isException) { onError( `Received error while checking if react has loaded: ${value}`, ); return; } if (isError) { onError( `Received error with code ${code} while checking if react has loaded: "${description}"`, ); return; } } if (pageHasReact) { onSuccess(); return; } onError(new CouldNotFindReactOnThePageError()); }, ); } // Just a Promise wrapper around `checkIfReactPresentInInspectedWindow` // returns a Promise, which will resolve only if React has been found on the page function poll(attempt) { return new Promise((resolve, reject) => { checkIfReactPresentInInspectedWindow(resolve, reject); }).catch(error => { if (error instanceof CouldNotFindReactOnThePageError) { if (attempt === attemptsThreshold) { onCouldNotFindReactAfterReachingAttemptsThreshold(); } // Start next attempt in 0.5s return new Promise(r => setTimeout(r, 500)).then(() => poll(attempt + 1), ); } // Propagating every other Error throw error; }); } poll(1) .then(onReactFound) .catch(error => { // Log propagated errors only if polling was not aborted // Some errors are expected when user performs in-tab navigation and `.eval()` is still being executed if (status === 'aborted') { return; } console.error(error); }); return {abort}; }