| import {isInjectRule, Operator, Rule, getUrlParameter} from './utils'; |
| |
| declare global { |
| interface Window { |
| ENABLED_EXPERIMENGTS?: string[]; |
| } |
| } |
| |
| function nextTick(ts: number) { |
| return new Promise(resolve => { |
| setTimeout(resolve, ts); |
| }); |
| } |
| |
| // Since content-script can not access window.Gerrit, |
| // here checks the readiness based on #mainHeader element |
| // Wait at most 5s before considering it as loaded. |
| const MAX_WAIT_TIME = 5000; |
| const getHeaderEl = () => { |
| if (document.querySelector('#app')) { |
| return document |
| .querySelector('#app') |
| .shadowRoot.querySelector('#app-element') |
| .shadowRoot.querySelector('#mainHeader'); |
| } else { |
| return document |
| .querySelector('gr-app') |
| .shadowRoot.querySelector('gr-app-element') |
| .shadowRoot.querySelector('gr-main-header'); |
| } |
| }; |
| const onGerritReady = async () => { |
| let header; |
| try { |
| header = getHeaderEl(); |
| } catch (e) {} |
| |
| let waitTime = 0; |
| while (!header) { |
| if (waitTime > MAX_WAIT_TIME) break; |
| waitTime += 1000; |
| await nextTick(1000); |
| try { |
| header = getHeaderEl(); |
| } catch (e) {} |
| } |
| return true; |
| }; |
| |
| let numOfSnackBars = 0; |
| function createSnackBar(message: string) { |
| const errorSnack = document.createElement('div'); |
| errorSnack.style.position = 'absolute'; |
| errorSnack.style.top = `${numOfSnackBars * 40}px`; |
| errorSnack.style.right = '10px'; |
| errorSnack.style.backgroundColor = 'black'; |
| errorSnack.style.color = 'white'; |
| errorSnack.style.padding = '10px'; |
| errorSnack.style.zIndex = '100'; |
| errorSnack.innerHTML = message; |
| document.body.appendChild(errorSnack); |
| numOfSnackBars++; |
| return errorSnack; |
| } |
| |
| // Apply injection rules to Gerrit sites if enabled |
| chrome.runtime.sendMessage({type: 'isEnabled'}, isEnabled => { |
| if (!isEnabled) return; |
| |
| chrome.storage.sync.get(['rules'], result => { |
| if (!result['rules']) return; |
| const rules = result['rules'] as Rule[]; |
| |
| // load |
| rules.filter(isInjectRule).forEach(rule => { |
| if (rule.disabled) return; |
| if (rule.operator === Operator.INJECT_HTML_PLUGIN) { |
| onGerritReady().then(() => { |
| const link = document.createElement('link'); |
| link.href = rule.destination; |
| link.rel = 'import'; |
| document.head.appendChild(link); |
| }); |
| } else if (rule.operator === Operator.INJECT_HTML_CODE) { |
| const el = document.createElement('div'); |
| el.innerHTML = rule.destination; |
| document.body.appendChild(el); |
| } else if (rule.operator === Operator.INJECT_JS_PLUGIN) { |
| onGerritReady().then(() => { |
| const link = document.createElement('script'); |
| link.setAttribute('src', rule.destination); |
| link.setAttribute('crossorigin', 'anonymous'); |
| document.head.appendChild(link); |
| }); |
| } else if (rule.operator === Operator.INJECT_JS_MODULE_PLUGIN) { |
| onGerritReady().then(() => { |
| const link = document.createElement('script'); |
| link.setAttribute('type', 'module'); |
| link.setAttribute('src', rule.destination); |
| link.setAttribute('crossorigin', 'anonymous'); |
| document.head.appendChild(link); |
| }); |
| } else if (rule.operator === Operator.INJECT_JS_CODE) { |
| const link = document.createElement('script'); |
| link.innerHTML = rule.destination; |
| document.head.appendChild(link); |
| } else if (rule.operator === Operator.INJECT_EXP) { |
| const exps = getUrlParameter('experiment'); |
| const hasSeachString = !!window.location.search; |
| const injectedExpNotInExps = new Set( |
| rule.destination |
| .trim() |
| .split(',') |
| .filter(exp => !exps.includes(exp.trim())) |
| ); |
| if (injectedExpNotInExps.size) { |
| const addedParams = [...injectedExpNotInExps].reduce( |
| (str, exp) => (str += `experiment=${exp}&`), |
| '' |
| ); |
| window.location.href += hasSeachString |
| ? `&${addedParams}` |
| : `?${addedParams}`; |
| } |
| } |
| }); |
| |
| // test redirect rules |
| rules |
| .filter(rule => !isInjectRule(rule)) |
| .forEach(rule => { |
| if ( |
| rule.operator === Operator.REDIRECT && |
| !rule.disabled && |
| // only test for js/html |
| /\.(js|html)+$/.test(rule.destination) |
| ) { |
| fetch(rule.destination) |
| .then(res => { |
| if (res.status < 200 || res.status >= 300) |
| throw new Error('Resource not found'); |
| }) |
| .catch(e => { |
| const errorSnack = createSnackBar( |
| `You may have an invalid redirect rule from ${rule.target} to ${rule.destination}` |
| ); |
| |
| // in case body is unresolved |
| document.body.style.display = 'block'; |
| document.body.style.opacity = '1'; |
| |
| setTimeout(() => { |
| errorSnack.remove(); |
| numOfSnackBars--; |
| }, 10 * 1000); |
| }); |
| } |
| }); |
| }); |
| }); |