blob: 7e3071a05394df16a5c1ee2ee926067b52a2aeee [file] [log] [blame]
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
const onGerritReady = async () => {
let header;
try {
header = document.querySelector("#app").shadowRoot.querySelector("#app-element").shadowRoot.querySelector("#mainHeader");
} catch (e) { }
while (!header) {
await nextTick(1000);
try {
header = document.querySelector("#app").shadowRoot.querySelector("#app-element").shadowRoot.querySelector("#mainHeader");
} 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);
});
}
});
});
});