blob: 66c3bbafb87f68478afdeae9f5185f2e26371e0b [file] [log] [blame]
import {Operator, getUrlParameter} from './utils';
import {StorageUtil} from './storage';
const link = document.createElement('script');
link.setAttribute('src', chrome.runtime.getURL('elements.js'));
document.head.appendChild(link);
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;
// Apply injection rules to Gerrit sites if enabled
chrome.runtime.sendMessage({type: 'isEnabled'}, async isEnabled => {
if (!isEnabled) return;
console.log('Gerrit FE Dev Helper is enabled.');
const tip = document.createElement('gdh-tip');
document.body.appendChild(tip);
const storage = new StorageUtil();
const rules = await storage.getRules();
for (const rule of rules) {
if (rule.disabled) continue;
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_CSS) {
onGerritReady().then(() => {
const link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
link.setAttribute('href', rule.destination);
document.head.appendChild(link);
});
} else if (rule.operator === Operator.INJECT_EXP) {
const exps = getUrlParameter('experiment');
const hasSearchString = !!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 += hasSearchString
? `&${addedParams}`
: `?${addedParams}`;
}
}
}
// Test redirect rules. Show a warning, if they are obviously not working as intended.
for (const rule of rules) {
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 message = `You may have an invalid redirect rule from ${rule.target} to ${rule.destination}`;
const id = `snack-${numOfSnackBars}`;
const snackbar = document.createElement('gdh-snackbar');
snackbar.id = id;
snackbar.setAttribute('message', message);
snackbar.setAttribute('position', `${numOfSnackBars}`);
document.body.appendChild(snackbar);
numOfSnackBars++;
// in case body is unresolved
document.body.style.display = 'block';
document.body.style.opacity = '1';
setTimeout(() => {
document.getElementById(id)?.remove();
numOfSnackBars--;
}, 10 * 1000);
});
}
}
});