ultraviolet/src/rewrite/rewrite.html.js

321 lines
7.9 KiB
JavaScript

/**
* @typedef {import('./index').default} Ultraviolet
*/
/**
*
* @param {Ultraviolet} ctx
*/
export function attributes(ctx, meta = ctx.meta) {
const { html, js, attributePrefix } = ctx;
const origPrefix = attributePrefix + '-attr-';
html.on('attr', (attr, type) => {
if (
attr.node.tagName === 'base' &&
attr.name === 'href' &&
attr.options.document
) {
meta.base = new URL(attr.value, meta.url);
}
if (type === 'rewrite' && isUrl(attr.name, attr.tagName)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = ctx.rewriteUrl(attr.value, meta);
}
if (type === 'rewrite' && isSrcset(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = html.wrapSrcset(attr.value, meta);
}
if (type === 'rewrite' && isHtml(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = html.rewrite(attr.value, {
...meta,
document: true,
injectHead: attr.options.injectHead || [],
});
}
if (type === 'rewrite' && isStyle(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = ctx.rewriteCSS(attr.value, {
context: 'declarationList',
});
}
if (type === 'rewrite' && isForbidden(attr.name)) {
attr.name = origPrefix + attr.name;
}
if (type === 'rewrite' && isEvent(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = js.rewrite(attr.value, meta);
}
if (type === 'source' && attr.name.startsWith(origPrefix)) {
if (attr.node.hasAttribute(attr.name.slice(origPrefix.length)))
attr.node.removeAttribute(attr.name.slice(origPrefix.length));
attr.name = attr.name.slice(origPrefix.length);
}
/*
if (isHtml(attr.name)) {
};
if (isStyle(attr.name)) {
};
if (isSrcset(attr.name)) {
};
*/
});
}
/**
*
* @param {Ultraviolet} ctx
*/
export function text(ctx) {
const { html, js, css } = ctx;
html.on('text', (text, type) => {
if (text.element.tagName === 'script') {
text.value =
type === 'rewrite'
? js.rewrite(text.value)
: js.source(text.value);
}
if (text.element.tagName === 'style') {
text.value =
type === 'rewrite'
? css.rewrite(text.value)
: css.source(text.value);
}
});
return true;
}
export function isUrl(name, tag) {
return (
(tag === 'object' && name === 'data') ||
[
'src',
'href',
'ping',
'movie',
'action',
'poster',
'profile',
'background',
].indexOf(name) > -1
);
}
export function isEvent(name) {
return (
[
'onafterprint',
'onbeforeprint',
'onbeforeunload',
'onerror',
'onhashchange',
'onload',
'onmessage',
'onoffline',
'ononline',
'onpagehide',
'onpopstate',
'onstorage',
'onunload',
'onblur',
'onchange',
'oncontextmenu',
'onfocus',
'oninput',
'oninvalid',
'onreset',
'onsearch',
'onselect',
'onsubmit',
'onkeydown',
'onkeypress',
'onkeyup',
'onclick',
'ondblclick',
'onmousedown',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onwheel',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'onscroll',
'oncopy',
'oncut',
'onpaste',
'onabort',
'oncanplay',
'oncanplaythrough',
'oncuechange',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onpause',
'onplay',
'onplaying',
'onprogress',
'onratechange',
'onseeked',
'onseeking',
'onstalled',
'onsuspend',
'ontimeupdate',
'onvolumechange',
'onwaiting',
].indexOf(name) > -1
);
}
/**
*
* @param {Ultraviolet} ctx
*/
export function injectHead(ctx) {
const { html } = ctx;
html.on('element', (element, type) => {
if (type !== 'rewrite') return false;
if (element.tagName !== 'head') return false;
if (!('injectHead' in element.options)) return false;
element.childNodes.unshift(...element.options.injectHead);
});
}
export function createJsInject(
cookies = '',
referrer = ''
) {
return (
`self.__uv$cookies = ${JSON.stringify(cookies)};` +
`self.__uv$referrer = ${JSON.stringify(referrer)};`
);
}
export function createHtmlInject(
handlerScript,
bundleScript,
clientScript,
configScript,
cookies,
referrer
) {
return [
{
tagName: 'script',
nodeName: 'script',
childNodes: [
{
nodeName: '#text',
value: createJsInject(cookies, referrer),
},
],
attrs: [
{
name: '__uv-script',
value: '1',
skip: true,
},
],
skip: true,
},
{
tagName: 'script',
nodeName: 'script',
childNodes: [],
attrs: [
{ name: 'src', value: bundleScript, skip: true },
{
name: '__uv-script',
value: '1',
skip: true,
},
],
},
{
tagName: 'script',
nodeName: 'script',
childNodes: [],
attrs: [
{ name: 'src', value: clientScript, skip: true },
{
name: '__uv-script',
value: '1',
skip: true,
},
],
},
{
tagName: 'script',
nodeName: 'script',
childNodes: [],
attrs: [
{ name: 'src', value: configScript, skip: true },
{
name: '__uv-script',
value: '1',
skip: true,
},
],
},
{
tagName: 'script',
nodeName: 'script',
childNodes: [],
attrs: [
{ name: 'src', value: handlerScript, skip: true },
{
name: '__uv-script',
value: '1',
skip: true,
},
],
},
];
}
export function isForbidden(name) {
return (
['http-equiv', 'integrity', 'sandbox', 'nonce', 'crossorigin'].indexOf(
name
) > -1
);
}
export function isHtml(name) {
return name === 'srcdoc';
}
export function isStyle(name) {
return name === 'style';
}
export function isSrcset(name) {
return name === 'srcset' || name === 'imagesrcset';
}