This commit is contained in:
David Reed 2022-11-18 20:00:58 -05:00
parent d230e0e551
commit 8f999e7236
No known key found for this signature in database
GPG key ID: 2211691D8A1EE72F
42 changed files with 10481 additions and 8966 deletions

View file

@ -4,39 +4,45 @@ about: Create a report to help us improve
title: '' title: ''
labels: '' labels: ''
assignees: '' assignees: ''
--- ---
Issue tracker is **ONLY** used for reporting bugs. New features should be discussed on our Discord server. Issue tracker is **ONLY** used for reporting bugs. New features should be discussed on our Discord server.
<!--- Provide a general summary of the issue in the Title above --> <!--- Provide a general summary of the issue in the Title above -->
## Expected Behavior ## Expected Behavior
<!--- Tell us what should happen --> <!--- Tell us what should happen -->
## Current Behavior ## Current Behavior
<!--- Tell us what happens instead of the expected behavior --> <!--- Tell us what happens instead of the expected behavior -->
## Possible Solution ## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, --> <!--- Not obligatory, but suggest a fix/reason for the bug, -->
## Steps to Reproduce ## Steps to Reproduce
<!--- Provide a link to a live example, or an unambiguous set of steps to --> <!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant --> <!--- reproduce this bug. Include code to reproduce, if relevant -->
1. 1.
2. 2.
3. 3.
4. 4.
## Context (Environment) ## Context (Environment)
<!--- How has this issue affected you? What are you trying to accomplish? --> <!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world --> <!--- Providing context helps us come up with a solution that is most useful in the real world -->
<!--- Provide a general summary of the issue in the Title above --> <!--- Provide a general summary of the issue in the Title above -->
## Detailed Description ## Detailed Description
<!--- Provide a detailed description of the change or addition you are proposing --> <!--- Provide a detailed description of the change or addition you are proposing -->
## Possible Implementation ## Possible Implementation
<!--- Not obligatory, but suggest an idea for implementing addition or change --> <!--- Not obligatory, but suggest an idea for implementing addition or change -->

4
.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"tabWidth": 4,
"singleQuote": true
}

22
package-lock.json generated
View file

@ -22,6 +22,7 @@
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.8.0", "eslint": "^8.8.0",
"prettier": "^2.7.1",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"webpack": "^5.74.0", "webpack": "^5.74.0",
"webpack-cli": "^4.10.0" "webpack-cli": "^4.10.0"
@ -1996,6 +1997,21 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@ -4192,6 +4208,12 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true "dev": true
}, },
"prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"dev": true
},
"punycode": { "punycode": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",

View file

@ -25,6 +25,7 @@
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.8.0", "eslint": "^8.8.0",
"prettier": "^2.7.1",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"webpack": "^5.74.0", "webpack": "^5.74.0",
"webpack-cli": "^4.10.0" "webpack-cli": "^4.10.0"

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class AttrApi extends EventEmitter { class AttrApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -8,8 +8,14 @@ class AttrApi extends EventEmitter {
this.window = ctx.window; this.window = ctx.window;
this.Attr = this.window.Attr || {}; this.Attr = this.window.Attr || {};
this.attrProto = this.Attr.prototype || {}; this.attrProto = this.Attr.prototype || {};
this.value = ctx.nativeMethods.getOwnPropertyDescriptor(this.attrProto, 'value'); this.value = ctx.nativeMethods.getOwnPropertyDescriptor(
this.name = ctx.nativeMethods.getOwnPropertyDescriptor(this.attrProto, 'name'); this.attrProto,
'value'
);
this.name = ctx.nativeMethods.getOwnPropertyDescriptor(
this.attrProto,
'name'
);
this.getNamedItem = this.attrProto.getNamedItem || null; this.getNamedItem = this.attrProto.getNamedItem || null;
this.setNamedItem = this.attrProto.setNamedItem || null; this.setNamedItem = this.attrProto.setNamedItem || null;
this.removeNamedItem = this.attrProto.removeNamedItem || null; this.removeNamedItem = this.attrProto.removeNamedItem || null;
@ -17,11 +23,15 @@ class AttrApi extends EventEmitter {
this.setNamedItemNS = this.attrProto.setNamedItemNS || null; this.setNamedItemNS = this.attrProto.setNamedItemNS || null;
this.removeNamedItemNS = this.attrProto.removeNamedItemNS || null; this.removeNamedItemNS = this.attrProto.removeNamedItemNS || null;
this.item = this.attrProto.item || null; this.item = this.attrProto.item || null;
}; }
overrideNameValue() { overrideNameValue() {
this.ctx.overrideDescriptor(this.attrProto, 'name', { this.ctx.overrideDescriptor(this.attrProto, 'name', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('name', event); this.emit('name', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -31,55 +41,82 @@ class AttrApi extends EventEmitter {
this.ctx.overrideDescriptor(this.attrProto, 'value', { this.ctx.overrideDescriptor(this.attrProto, 'value', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ name: this.name.get.call(that), value: target.call(that) }, target, that); const event = new HookEvent(
{
name: this.name.get.call(that),
value: target.call(that),
},
target,
that
);
this.emit('getValue', event); this.emit('getValue', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ val ]) => { set: (target, that, [val]) => {
const event = new HookEvent({ name: this.name.get.call(that), value: val }, target, that); const event = new HookEvent(
{ name: this.name.get.call(that), value: val },
target,
that
);
this.emit('setValue', event); this.emit('setValue', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
event.target.call(event.that, event.data.value); event.target.call(event.that, event.data.value);
} },
}); });
}; }
overrideItemMethods() { overrideItemMethods() {
this.ctx.override(this.attrProto, 'getNamedItem', (target, that, args) => { this.ctx.override(
this.attrProto,
'getNamedItem',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name }, target, that); const event = new HookEvent({ name }, target, that);
this.emit('getNamedItem', event); this.emit('getNamedItem', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
this.ctx.override(this.attrProto, 'setNamedItem', (target, that, args) => { );
this.ctx.override(
this.attrProto,
'setNamedItem',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args; let [name, value] = args;
const event = new HookEvent({ name, value }, target, that); const event = new HookEvent({ name, value }, target, that);
this.emit('setNamedItem', event); this.emit('setNamedItem', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value); return event.target.call(
}); event.that,
this.ctx.override(this.attrProto, 'removeNamedItem', (target, that, args) => { event.data.name,
event.data.value
);
}
);
this.ctx.override(
this.attrProto,
'removeNamedItem',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name }, target, that); const event = new HookEvent({ name }, target, that);
this.emit('removeNamedItem', event); this.emit('removeNamedItem', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
);
this.ctx.override(this.attrProto, 'item', (target, that, args) => { this.ctx.override(this.attrProto, 'item', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ index ] = args; let [index] = args;
const event = new HookEvent({ index }, target, that); const event = new HookEvent({ index }, target, that);
this.emit('item', event); this.emit('item', event);
@ -87,37 +124,65 @@ class AttrApi extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); });
this.ctx.override(this.attrProto, 'getNamedItemNS', (target, that, args) => { this.ctx.override(
this.attrProto,
'getNamedItemNS',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ namespace, localName ] = args; let [namespace, localName] = args;
const event = new HookEvent({ namespace, localName }, target, that); const event = new HookEvent(
{ namespace, localName },
target,
that
);
this.emit('getNamedItemNS', event); this.emit('getNamedItemNS', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.namespace, event.data.localName); return event.target.call(
}); event.that,
this.ctx.override(this.attrProto, 'setNamedItemNS', (target, that, args) => { event.data.namespace,
event.data.localName
);
}
);
this.ctx.override(
this.attrProto,
'setNamedItemNS',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ attr ] = args; let [attr] = args;
const event = new HookEvent({ attr }, target, that); const event = new HookEvent({ attr }, target, that);
this.emit('setNamedItemNS', event); this.emit('setNamedItemNS', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
this.ctx.override(this.attrProto, 'removeNamedItemNS', (target, that, args) => { );
this.ctx.override(
this.attrProto,
'removeNamedItemNS',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ namespace, localName ] = args; let [namespace, localName] = args;
const event = new HookEvent({ namespace, localName }, target, that); const event = new HookEvent(
{ namespace, localName },
target,
that
);
this.emit('removeNamedItemNS', event); this.emit('removeNamedItemNS', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.namespace, event.data.localName); return event.target.call(
}); event.that,
}; event.data.namespace,
}; event.data.localName
);
}
);
}
}
export default AttrApi; export default AttrApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class DocumentHook extends EventEmitter { class DocumentHook extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -11,52 +11,86 @@ class DocumentHook extends EventEmitter {
this.DOMParser = this.window.DOMParser || {}; this.DOMParser = this.window.DOMParser || {};
this.docProto = this.Document.prototype || {}; this.docProto = this.Document.prototype || {};
this.domProto = this.DOMParser.prototype || {}; this.domProto = this.DOMParser.prototype || {};
this.title = ctx.nativeMethods.getOwnPropertyDescriptor(this.docProto, 'title'); this.title = ctx.nativeMethods.getOwnPropertyDescriptor(
this.cookie = ctx.nativeMethods.getOwnPropertyDescriptor(this.docProto, 'cookie'); this.docProto,
this.referrer = ctx.nativeMethods.getOwnPropertyDescriptor(this.docProto, 'referrer'); 'title'
this.domain = ctx.nativeMethods.getOwnPropertyDescriptor(this.docProto, 'domain'); );
this.documentURI = ctx.nativeMethods.getOwnPropertyDescriptor(this.docProto, 'documentURI'); this.cookie = ctx.nativeMethods.getOwnPropertyDescriptor(
this.docProto,
'cookie'
);
this.referrer = ctx.nativeMethods.getOwnPropertyDescriptor(
this.docProto,
'referrer'
);
this.domain = ctx.nativeMethods.getOwnPropertyDescriptor(
this.docProto,
'domain'
);
this.documentURI = ctx.nativeMethods.getOwnPropertyDescriptor(
this.docProto,
'documentURI'
);
this.write = this.docProto.write; this.write = this.docProto.write;
this.writeln = this.docProto.writeln; this.writeln = this.docProto.writeln;
this.querySelector = this.docProto.querySelector; this.querySelector = this.docProto.querySelector;
this.querySelectorAll = this.docProto.querySelectorAll; this.querySelectorAll = this.docProto.querySelectorAll;
this.parseFromString = this.domProto.parseFromString; this.parseFromString = this.domProto.parseFromString;
this.URL = ctx.nativeMethods.getOwnPropertyDescriptor(this.docProto, 'URL'); this.URL = ctx.nativeMethods.getOwnPropertyDescriptor(
}; this.docProto,
'URL'
);
}
overrideParseFromString() { overrideParseFromString() {
this.ctx.override(this.domProto, 'parseFromString', (target, that, args) => { this.ctx.override(
this.domProto,
'parseFromString',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ string, type ] = args; let [string, type] = args;
const event = new HookEvent({ string, type }, target, that); const event = new HookEvent({ string, type }, target, that);
this.emit('parseFromString', event); this.emit('parseFromString', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.string, event.data.type); return event.target.call(
}); event.that,
}; event.data.string,
event.data.type
);
}
);
}
overrideQuerySelector() { overrideQuerySelector() {
this.ctx.override(this.docProto, 'querySelector', (target, that, args) => { this.ctx.override(
this.docProto,
'querySelector',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ selectors ] = args; let [selectors] = args;
const event = new HookEvent({ selectors }, target, that); const event = new HookEvent({ selectors }, target, that);
this.emit('querySelector', event); this.emit('querySelector', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.selectors); return event.target.call(event.that, event.data.selectors);
}); }
}; );
}
overrideDomain() { overrideDomain() {
this.ctx.overrideDescriptor(this.docProto, 'domain', { this.ctx.overrideDescriptor(this.docProto, 'domain', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getDomain', event); this.emit('getDomain', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ val ]) => { set: (target, that, [val]) => {
const event = new HookEvent({ value: val }, target, that); const event = new HookEvent({ value: val }, target, that);
this.emit('setDomain', event); this.emit('setDomain', event);
@ -64,34 +98,53 @@ class DocumentHook extends EventEmitter {
return event.target.call(event.that, event.data.value); return event.target.call(event.that, event.data.value);
}, },
}); });
}; }
overrideReferrer() { overrideReferrer() {
this.ctx.overrideDescriptor(this.docProto, 'referrer', { this.ctx.overrideDescriptor(this.docProto, 'referrer', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('referrer', event); this.emit('referrer', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
}); });
}; }
overrideCreateTreeWalker() { overrideCreateTreeWalker() {
this.ctx.override(this.docProto, 'createTreeWalker', (target, that, args) => { this.ctx.override(
this.docProto,
'createTreeWalker',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ root, show = 0xFFFFFFFF, filter, expandEntityReferences ] = args; let [root, show = 0xffffffff, filter, expandEntityReferences] =
args;
const event = new HookEvent({ root, show, filter, expandEntityReferences }, target, that); const event = new HookEvent(
{ root, show, filter, expandEntityReferences },
target,
that
);
this.emit('createTreeWalker', event); this.emit('createTreeWalker', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.root, event.data.show, event.data.filter, event.data.expandEntityReferences); return event.target.call(
}); event.that,
}; event.data.root,
event.data.show,
event.data.filter,
event.data.expandEntityReferences
);
}
);
}
overrideWrite() { overrideWrite() {
this.ctx.override(this.docProto, 'write', (target, that, args) => { this.ctx.override(this.docProto, 'write', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ ...html ] = args; let [...html] = args;
const event = new HookEvent({ html }, target, that); const event = new HookEvent({ html }, target, that);
this.emit('write', event); this.emit('write', event);
@ -101,7 +154,7 @@ class DocumentHook extends EventEmitter {
}); });
this.ctx.override(this.docProto, 'writeln', (target, that, args) => { this.ctx.override(this.docProto, 'writeln', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ ...html ] = args; let [...html] = args;
const event = new HookEvent({ html }, target, that); const event = new HookEvent({ html }, target, that);
this.emit('writeln', event); this.emit('writeln', event);
@ -109,76 +162,96 @@ class DocumentHook extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.apply(event.that, event.data.html); return event.target.apply(event.that, event.data.html);
}); });
}; }
overrideDocumentURI() { overrideDocumentURI() {
this.ctx.overrideDescriptor(this.docProto, 'documentURI', { this.ctx.overrideDescriptor(this.docProto, 'documentURI', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('documentURI', event); this.emit('documentURI', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
}); });
}; }
overrideURL() { overrideURL() {
this.ctx.overrideDescriptor(this.docProto, 'URL', { this.ctx.overrideDescriptor(this.docProto, 'URL', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('url', event); this.emit('url', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
}); });
}; }
overrideReferrer() { overrideReferrer() {
this.ctx.overrideDescriptor(this.docProto, 'referrer', { this.ctx.overrideDescriptor(this.docProto, 'referrer', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('referrer', event); this.emit('referrer', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
}); });
}; }
overrideCookie() { overrideCookie() {
this.ctx.overrideDescriptor(this.docProto, 'cookie', { this.ctx.overrideDescriptor(this.docProto, 'cookie', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getCookie', event); this.emit('getCookie', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ value ]) => { set: (target, that, [value]) => {
const event = new HookEvent({ value, }, target, that); const event = new HookEvent({ value }, target, that);
this.emit('setCookie', event); this.emit('setCookie', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.value); return event.target.call(event.that, event.data.value);
}, },
}); });
}; }
overrideTitle() { overrideTitle() {
this.ctx.overrideDescriptor(this.docProto, 'title', { this.ctx.overrideDescriptor(this.docProto, 'title', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getTitle', event); this.emit('getTitle', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ value ]) => { set: (target, that, [value]) => {
const event = new HookEvent({ value, }, target, that); const event = new HookEvent({ value }, target, that);
this.emit('setTitle', event); this.emit('setTitle', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.value); return event.target.call(event.that, event.data.value);
}, },
}); });
}; }
}; }
export default DocumentHook; export default DocumentHook;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class ElementApi extends EventEmitter { class ElementApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -9,8 +9,14 @@ class ElementApi extends EventEmitter {
this.Audio = this.window.Audio; this.Audio = this.window.Audio;
this.Element = this.window.Element; this.Element = this.window.Element;
this.elemProto = this.Element ? this.Element.prototype : {}; this.elemProto = this.Element ? this.Element.prototype : {};
this.innerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(this.elemProto, 'innerHTML'); this.innerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(
this.outerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(this.elemProto, 'outerHTML'); this.elemProto,
'innerHTML'
);
this.outerHTML = ctx.nativeMethods.getOwnPropertyDescriptor(
this.elemProto,
'outerHTML'
);
this.setAttribute = this.elemProto.setAttribute; this.setAttribute = this.elemProto.setAttribute;
this.getAttribute = this.elemProto.getAttribute; this.getAttribute = this.elemProto.getAttribute;
this.removeAttribute = this.elemProto.removeAttribute; this.removeAttribute = this.elemProto.removeAttribute;
@ -19,83 +25,116 @@ class ElementApi extends EventEmitter {
this.querySelectorAll = this.elemProto.querySelectorAll; this.querySelectorAll = this.elemProto.querySelectorAll;
this.insertAdjacentHTML = this.elemProto.insertAdjacentHTML; this.insertAdjacentHTML = this.elemProto.insertAdjacentHTML;
this.insertAdjacentText = this.elemProto.insertAdjacentText; this.insertAdjacentText = this.elemProto.insertAdjacentText;
}; }
overrideQuerySelector() { overrideQuerySelector() {
this.ctx.override(this.elemProto, 'querySelector', (target, that, args) => { this.ctx.override(
this.elemProto,
'querySelector',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ selectors ] = args; let [selectors] = args;
const event = new HookEvent({ selectors }, target, that); const event = new HookEvent({ selectors }, target, that);
this.emit('querySelector', event); this.emit('querySelector', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.selectors); return event.target.call(event.that, event.data.selectors);
}); }
}; );
}
overrideAttribute() { overrideAttribute() {
this.ctx.override(this.elemProto, 'getAttribute', (target, that, args) => { this.ctx.override(
this.elemProto,
'getAttribute',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name }, target, that); const event = new HookEvent({ name }, target, that);
this.emit('getAttribute', event); this.emit('getAttribute', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
this.ctx.override(this.elemProto, 'setAttribute', (target, that, args) => { );
this.ctx.override(
this.elemProto,
'setAttribute',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args; let [name, value] = args;
const event = new HookEvent({ name, value }, target, that); const event = new HookEvent({ name, value }, target, that);
this.emit('setAttribute', event); this.emit('setAttribute', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value); return event.target.call(
}); event.that,
this.ctx.override(this.elemProto, 'hasAttribute', (target, that, args) => { event.data.name,
event.data.value
);
}
);
this.ctx.override(
this.elemProto,
'hasAttribute',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name }, target, that); const event = new HookEvent({ name }, target, that);
this.emit('hasAttribute', event); this.emit('hasAttribute', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
this.ctx.override(this.elemProto, 'removeAttribute', (target, that, args) => { );
this.ctx.override(
this.elemProto,
'removeAttribute',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name }, target, that); const event = new HookEvent({ name }, target, that);
this.emit('removeAttribute', event); this.emit('removeAttribute', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
}; );
}
overrideAudio() { overrideAudio() {
this.ctx.override(this.window, 'Audio', (target, that, args) => { this.ctx.override(
this.window,
'Audio',
(target, that, args) => {
if (!args.length) return new target(...args); if (!args.length) return new target(...args);
let [ url ] = args; let [url] = args;
const event = new HookEvent({ url }, target, that); const event = new HookEvent({ url }, target, that);
this.emit('audio', event); this.emit('audio', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return new event.target(event.data.url); return new event.target(event.data.url);
}, true); },
}; true
);
}
overrideHtml() { overrideHtml() {
this.hookProperty(this.Element, 'innerHTML', { this.hookProperty(this.Element, 'innerHTML', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getInnerHTML', event); this.emit('getInnerHTML', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ val ]) => { set: (target, that, [val]) => {
const event = new HookEvent({ value: val }, target, that); const event = new HookEvent({ value: val }, target, that);
this.emit('setInnerHTML', event); this.emit('setInnerHTML', event);
@ -105,13 +144,17 @@ class ElementApi extends EventEmitter {
}); });
this.hookProperty(this.Element, 'outerHTML', { this.hookProperty(this.Element, 'outerHTML', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getOuterHTML', event); this.emit('getOuterHTML', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ val ]) => { set: (target, that, [val]) => {
const event = new HookEvent({ value: val }, target, that); const event = new HookEvent({ value: val }, target, that);
this.emit('setOuterHTML', event); this.emit('setOuterHTML', event);
@ -119,47 +162,63 @@ class ElementApi extends EventEmitter {
target.call(that, event.data.value); target.call(that, event.data.value);
}, },
}); });
}; }
overrideInsertAdjacentHTML() { overrideInsertAdjacentHTML() {
this.ctx.override(this.elemProto, 'insertAdjacentHTML', (target, that, args) => { this.ctx.override(
this.elemProto,
'insertAdjacentHTML',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ position, html ] = args; let [position, html] = args;
const event = new HookEvent({ position, html }, target, that); const event = new HookEvent({ position, html }, target, that);
this.emit('insertAdjacentHTML', event); this.emit('insertAdjacentHTML', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.position, event.data.html); return event.target.call(
}); event.that,
}; event.data.position,
event.data.html
);
}
);
}
overrideInsertAdjacentText() { overrideInsertAdjacentText() {
this.ctx.override(this.elemProto, 'insertAdjacentText', (target, that, args) => { this.ctx.override(
this.elemProto,
'insertAdjacentText',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ position, text ] = args; let [position, text] = args;
const event = new HookEvent({ position, text }, target, that); const event = new HookEvent({ position, text }, target, that);
this.emit('insertAdjacentText', event); this.emit('insertAdjacentText', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.position, event.data.text); return event.target.call(
}); event.that,
}; event.data.position,
event.data.text
);
}
);
}
hookProperty(element, prop, handler) { hookProperty(element, prop, handler) {
if (!element || !prop in element) return false; if (!element || !prop in element) return false;
if (this.ctx.nativeMethods.isArray(element)) { if (this.ctx.nativeMethods.isArray(element)) {
for (const elem of element) { for (const elem of element) {
this.hookProperty(elem, prop, handler); this.hookProperty(elem, prop, handler);
}; }
return true; return true;
}; }
const proto = element.prototype; const proto = element.prototype;
this.ctx.overrideDescriptor(proto, prop, handler); this.ctx.overrideDescriptor(proto, prop, handler);
return true; return true;
}; }
}; }
export default ElementApi; export default ElementApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class NodeApi extends EventEmitter { class NodeApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -16,24 +16,49 @@ class NodeApi extends EventEmitter {
this.appendChild = this.nodeProto.appendChild; this.appendChild = this.nodeProto.appendChild;
this.removeChild = this.nodeProto.removeChild; this.removeChild = this.nodeProto.removeChild;
this.textContent = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'textContent'); this.textContent = ctx.nativeMethods.getOwnPropertyDescriptor(
this.parentNode = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'parentNode'); this.nodeProto,
this.parentElement = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'parentElement'); 'textContent'
this.childNodes = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'childNodes'); );
this.baseURI = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'baseURI'); this.parentNode = ctx.nativeMethods.getOwnPropertyDescriptor(
this.previousSibling = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'previousSibling'); this.nodeProto,
this.ownerDocument = ctx.nativeMethods.getOwnPropertyDescriptor(this.nodeProto, 'ownerDocument'); 'parentNode'
}; );
this.parentElement = ctx.nativeMethods.getOwnPropertyDescriptor(
this.nodeProto,
'parentElement'
);
this.childNodes = ctx.nativeMethods.getOwnPropertyDescriptor(
this.nodeProto,
'childNodes'
);
this.baseURI = ctx.nativeMethods.getOwnPropertyDescriptor(
this.nodeProto,
'baseURI'
);
this.previousSibling = ctx.nativeMethods.getOwnPropertyDescriptor(
this.nodeProto,
'previousSibling'
);
this.ownerDocument = ctx.nativeMethods.getOwnPropertyDescriptor(
this.nodeProto,
'ownerDocument'
);
}
overrideTextContent() { overrideTextContent() {
this.ctx.overrideDescriptor(this.nodeProto, 'textContent', { this.ctx.overrideDescriptor(this.nodeProto, 'textContent', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getTextContent', event); this.emit('getTextContent', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ val ]) => { set: (target, that, [val]) => {
const event = new HookEvent({ value: val }, target, that); const event = new HookEvent({ value: val }, target, that);
this.emit('setTextContent', event); this.emit('setTextContent', event);
@ -41,41 +66,57 @@ class NodeApi extends EventEmitter {
target.call(that, event.data.value); target.call(that, event.data.value);
}, },
}); });
}; }
overrideAppend() { overrideAppend() {
this.ctx.override(this.nodeProto, 'append', (target, that, [ ...nodes ]) => { this.ctx.override(
this.nodeProto,
'append',
(target, that, [...nodes]) => {
const event = new HookEvent({ nodes }, target, that); const event = new HookEvent({ nodes }, target, that);
this.emit('append', event); this.emit('append', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.nodes); return event.target.call(event.that, event.data.nodes);
}); }
this.ctx.override(this.nodeProto, 'appendChild', (target, that, args) => { );
this.ctx.override(
this.nodeProto,
'appendChild',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ node ] = args; let [node] = args;
const event = new HookEvent({ node }, target, that); const event = new HookEvent({ node }, target, that);
this.emit('appendChild', event); this.emit('appendChild', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.node); return event.target.call(event.that, event.data.node);
}); }
}; );
}
overrideBaseURI() { overrideBaseURI() {
this.ctx.overrideDescriptor(this.nodeProto, 'baseURI', { this.ctx.overrideDescriptor(this.nodeProto, 'baseURI', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('baseURI', event); this.emit('baseURI', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
}) });
}; }
overrideParent() { overrideParent() {
this.ctx.overrideDescriptor(this.nodeProto, 'parentNode', { this.ctx.overrideDescriptor(this.nodeProto, 'parentNode', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ node: target.call(that) }, target, that); const event = new HookEvent(
{ node: target.call(that) },
target,
that
);
this.emit('parentNode', event); this.emit('parentNode', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -84,38 +125,50 @@ class NodeApi extends EventEmitter {
}); });
this.ctx.overrideDescriptor(this.nodeProto, 'parentElement', { this.ctx.overrideDescriptor(this.nodeProto, 'parentElement', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ element: target.call(that) }, target, that); const event = new HookEvent(
{ element: target.call(that) },
target,
that
);
this.emit('parentElement', event); this.emit('parentElement', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.node; return event.data.node;
}, },
}); });
}; }
overrideOwnerDocument() { overrideOwnerDocument() {
this.ctx.overrideDescriptor(this.nodeProto, 'ownerDocument', { this.ctx.overrideDescriptor(this.nodeProto, 'ownerDocument', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ document: target.call(that) }, target, that); const event = new HookEvent(
{ document: target.call(that) },
target,
that
);
this.emit('ownerDocument', event); this.emit('ownerDocument', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.document; return event.data.document;
}, },
}); });
}; }
overrideCompareDocumentPosit1ion() { overrideCompareDocumentPosit1ion() {
this.ctx.override(this.nodeProto, 'compareDocumentPosition', (target, that, args) => { this.ctx.override(
this.nodeProto,
'compareDocumentPosition',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ node ] = args; let [node] = args;
const event = new HookEvent({ node }, target, that); const event = new HookEvent({ node }, target, that);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.node); return event.target.call(event.that, event.data.node);
}); }
}; );
}
overrideChildMethods() { overrideChildMethods() {
this.ctx.override(this.nodeProto, 'removeChild') this.ctx.override(this.nodeProto, 'removeChild');
}; }
}; }
export default NodeApi; export default NodeApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class StyleApi extends EventEmitter { class StyleApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -10,9 +10,29 @@ class StyleApi extends EventEmitter {
this.cssStyleProto = this.CSSStyleDeclaration.prototype || {}; this.cssStyleProto = this.CSSStyleDeclaration.prototype || {};
this.getPropertyValue = this.cssStyleProto.getPropertyValue || null; this.getPropertyValue = this.cssStyleProto.getPropertyValue || null;
this.setProperty = this.cssStyleProto.setProperty || null; this.setProperty = this.cssStyleProto.setProperty || null;
this.cssText - ctx.nativeMethods.getOwnPropertyDescriptors(this.cssStyleProto, 'cssText'); this.cssText -
this.urlProps = ['background', 'backgroundImage', 'borderImage', 'borderImageSource', 'listStyle', 'listStyleImage', 'cursor']; ctx.nativeMethods.getOwnPropertyDescriptors(
this.dashedUrlProps = ['background', 'background-image', 'border-image', 'border-image-source', 'list-style', 'list-style-image', 'cursor']; this.cssStyleProto,
'cssText'
);
this.urlProps = [
'background',
'backgroundImage',
'borderImage',
'borderImageSource',
'listStyle',
'listStyleImage',
'cursor',
];
this.dashedUrlProps = [
'background',
'background-image',
'border-image',
'border-image-source',
'list-style',
'list-style-image',
'cursor',
];
this.propToDashed = { this.propToDashed = {
background: 'background', background: 'background',
backgroundImage: 'background-image', backgroundImage: 'background-image',
@ -20,42 +40,58 @@ class StyleApi extends EventEmitter {
borderImageSource: 'border-image-source', borderImageSource: 'border-image-source',
listStyle: 'list-style', listStyle: 'list-style',
listStyleImage: 'list-style-image', listStyleImage: 'list-style-image',
cursor: 'cursor' cursor: 'cursor',
};
}; };
}
overrideSetGetProperty() { overrideSetGetProperty() {
this.ctx.override(this.cssStyleProto, 'getPropertyValue', (target, that, args) => { this.ctx.override(
this.cssStyleProto,
'getPropertyValue',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ property ] = args; let [property] = args;
const event = new HookEvent({ property }, target, that); const event = new HookEvent({ property }, target, that);
this.emit('getPropertyValue', event); this.emit('getPropertyValue', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.property); return event.target.call(event.that, event.data.property);
}); }
this.ctx.override(this.cssStyleProto, 'setProperty', (target, that, args) => { );
this.ctx.override(
this.cssStyleProto,
'setProperty',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ property, value ] = args; let [property, value] = args;
const event = new HookEvent({ property, value }, target, that); const event = new HookEvent({ property, value }, target, that);
this.emit('setProperty', event); this.emit('setProperty', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.property, event.data.value); return event.target.call(
}); event.that,
}; event.data.property,
event.data.value
);
}
);
}
overrideCssText() { overrideCssText() {
this.ctx.overrideDescriptor(this.cssStyleProto, 'cssText', { this.ctx.overrideDescriptor(this.cssStyleProto, 'cssText', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getCssText', event); this.emit('getCssText', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
set: (target, that, [ val ]) => { set: (target, that, [val]) => {
const event = new HookEvent({ value: val }, target, that); const event = new HookEvent({ value: val }, target, that);
this.emit('setCssText', event); this.emit('setCssText', event);
@ -63,7 +99,7 @@ class StyleApi extends EventEmitter {
return event.target.call(event.that, event.data.value); return event.target.call(event.that, event.data.value);
}, },
}); });
}; }
}; }
export default StyleApi; export default StyleApi;

View file

@ -21,20 +21,22 @@
'use strict'; 'use strict';
var R = typeof Reflect === 'object' ? Reflect : null var R = typeof Reflect === 'object' ? Reflect : null;
var ReflectApply = R && typeof R.apply === 'function' var ReflectApply =
R && typeof R.apply === 'function'
? R.apply ? R.apply
: function ReflectApply(target, receiver, args) { : function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args); return Function.prototype.apply.call(target, receiver, args);
} };
var ReflectOwnKeys var ReflectOwnKeys;
if (R && typeof R.ownKeys === 'function') { if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys ReflectOwnKeys = R.ownKeys;
} else if (Object.getOwnPropertySymbols) { } else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) { ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target) return Object.getOwnPropertyNames(target).concat(
.concat(Object.getOwnPropertySymbols(target)); Object.getOwnPropertySymbols(target)
);
}; };
} else { } else {
ReflectOwnKeys = function ReflectOwnKeys(target) { ReflectOwnKeys = function ReflectOwnKeys(target) {
@ -46,9 +48,11 @@ function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning); if (console && console.warn) console.warn(warning);
} }
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { var NumberIsNaN =
Number.isNaN ||
function NumberIsNaN(value) {
return value !== value; return value !== value;
} };
function EventEmitter() { function EventEmitter() {
EventEmitter.init.call(this); EventEmitter.init.call(this);
@ -69,27 +73,35 @@ var defaultMaxListeners = 10;
function checkListener(listener) { function checkListener(listener) {
if (typeof listener !== 'function') { if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); throw new TypeError(
'The "listener" argument must be of type Function. Received type ' +
typeof listener
);
} }
} }
Object.defineProperty(EventEmitter, 'defaultMaxListeners', { Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true, enumerable: true,
get: function() { get: function () {
return defaultMaxListeners; return defaultMaxListeners;
}, },
set: function(arg) { set: function (arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); throw new RangeError(
'The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' +
arg +
'.'
);
} }
defaultMaxListeners = arg; defaultMaxListeners = arg;
} },
}); });
EventEmitter.init = function() { EventEmitter.init = function () {
if (
if (this._events === undefined || this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) { this._events === Object.getPrototypeOf(this)._events
) {
this._events = Object.create(null); this._events = Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} }
@ -101,7 +113,11 @@ EventEmitter.init = function() {
// that to be increased. Set to zero for unlimited. // that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); throw new RangeError(
'The value of "n" is out of range. It must be a non-negative number. Received ' +
n +
'.'
);
} }
this._maxListeners = n; this._maxListeners = n;
return this; return this;
@ -120,42 +136,39 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
EventEmitter.prototype.emit = function emit(type) { EventEmitter.prototype.emit = function emit(type) {
var args = []; var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error'); var doError = type === 'error';
var events = this._events; var events = this._events;
if (events !== undefined) if (events !== undefined) doError = doError && events.error === undefined;
doError = (doError && events.error === undefined); else if (!doError) return false;
else if (!doError)
return false;
// If there is no 'error' event listener then throw. // If there is no 'error' event listener then throw.
if (doError) { if (doError) {
var er; var er;
if (args.length > 0) if (args.length > 0) er = args[0];
er = args[0];
if (er instanceof Error) { if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show // Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception. // up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event throw er; // Unhandled 'error' event
} }
// At least give some kind of context to the user // At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); var err = new Error(
'Unhandled error.' + (er ? ' (' + er.message + ')' : '')
);
err.context = er; err.context = er;
throw err; // Unhandled 'error' event throw err; // Unhandled 'error' event
} }
var handler = events[type]; var handler = events[type];
if (handler === undefined) if (handler === undefined) return false;
return false;
if (typeof handler === 'function') { if (typeof handler === 'function') {
ReflectApply(handler, this, args); ReflectApply(handler, this, args);
} else { } else {
var len = handler.length; var len = handler.length;
var listeners = arrayClone(handler, len); var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i) for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
ReflectApply(listeners[i], this, args);
} }
return true; return true;
@ -176,8 +189,11 @@ function _addListener(target, type, listener, prepend) {
// To avoid recursion in the case that type === "newListener"! Before // To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener". // adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) { if (events.newListener !== undefined) {
target.emit('newListener', type, target.emit(
listener.listener ? listener.listener : listener); 'newListener',
type,
listener.listener ? listener.listener : listener
);
// Re-assign `events` because a newListener handler could have caused the // Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object // this._events to be assigned to a new object
@ -193,8 +209,9 @@ function _addListener(target, type, listener, prepend) {
} else { } else {
if (typeof existing === 'function') { if (typeof existing === 'function') {
// Adding the second element, need to change to array. // Adding the second element, need to change to array.
existing = events[type] = existing = events[type] = prepend
prepend ? [listener, existing] : [existing, listener]; ? [listener, existing]
: [existing, listener];
// If we've already got an array, just append. // If we've already got an array, just append.
} else if (prepend) { } else if (prepend) {
existing.unshift(listener); existing.unshift(listener);
@ -208,10 +225,15 @@ function _addListener(target, type, listener, prepend) {
existing.warned = true; existing.warned = true;
// No error code for this since it is a Warning // No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' + var w = new Error(
existing.length + ' ' + String(type) + ' listeners ' + 'Possible EventEmitter memory leak detected. ' +
existing.length +
' ' +
String(type) +
' listeners ' +
'added. Use emitter.setMaxListeners() to ' + 'added. Use emitter.setMaxListeners() to ' +
'increase limit'); 'increase limit'
);
w.name = 'MaxListenersExceededWarning'; w.name = 'MaxListenersExceededWarning';
w.emitter = target; w.emitter = target;
w.type = type; w.type = type;
@ -229,23 +251,30 @@ EventEmitter.prototype.addListener = function addListener(type, listener) {
EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener = EventEmitter.prototype.prependListener = function prependListener(
function prependListener(type, listener) { type,
listener
) {
return _addListener(this, type, listener, true); return _addListener(this, type, listener, true);
}; };
function onceWrapper() { function onceWrapper() {
if (!this.fired) { if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn); this.target.removeListener(this.type, this.wrapFn);
this.fired = true; this.fired = true;
if (arguments.length === 0) if (arguments.length === 0) return this.listener.call(this.target);
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments); return this.listener.apply(this.target, arguments);
} }
} }
function _onceWrap(target, type, listener) { function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; var state = {
fired: false,
wrapFn: undefined,
target: target,
type: type,
listener: listener,
};
var wrapped = onceWrapper.bind(state); var wrapped = onceWrapper.bind(state);
wrapped.listener = listener; wrapped.listener = listener;
state.wrapFn = wrapped; state.wrapFn = wrapped;
@ -258,31 +287,32 @@ EventEmitter.prototype.once = function once(type, listener) {
return this; return this;
}; };
EventEmitter.prototype.prependOnceListener = EventEmitter.prototype.prependOnceListener = function prependOnceListener(
function prependOnceListener(type, listener) { type,
listener
) {
checkListener(listener); checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener)); this.prependListener(type, _onceWrap(this, type, listener));
return this; return this;
}; };
// Emits a 'removeListener' event if and only if the listener was removed. // Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener = EventEmitter.prototype.removeListener = function removeListener(
function removeListener(type, listener) { type,
listener
) {
var list, events, position, i, originalListener; var list, events, position, i, originalListener;
checkListener(listener); checkListener(listener);
events = this._events; events = this._events;
if (events === undefined) if (events === undefined) return this;
return this;
list = events[type]; list = events[type];
if (list === undefined) if (list === undefined) return this;
return this;
if (list === listener || list.listener === listener) { if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) if (--this._eventsCount === 0) this._events = Object.create(null);
this._events = Object.create(null);
else { else {
delete events[type]; delete events[type];
if (events.removeListener) if (events.removeListener)
@ -299,34 +329,29 @@ EventEmitter.prototype.removeListener =
} }
} }
if (position < 0) if (position < 0) return this;
return this;
if (position === 0) if (position === 0) list.shift();
list.shift();
else { else {
spliceOne(list, position); spliceOne(list, position);
} }
if (list.length === 1) if (list.length === 1) events[type] = list[0];
events[type] = list[0];
if (events.removeListener !== undefined) if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener); this.emit('removeListener', type, originalListener || listener);
} }
return this; return this;
}; };
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
function removeAllListeners(type) {
var listeners, events, i; var listeners, events, i;
events = this._events; events = this._events;
if (events === undefined) if (events === undefined) return this;
return this;
// not listening for removeListener, no need to emit // not listening for removeListener, no need to emit
if (events.removeListener === undefined) { if (events.removeListener === undefined) {
@ -334,10 +359,8 @@ EventEmitter.prototype.removeAllListeners =
this._events = Object.create(null); this._events = Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} else if (events[type] !== undefined) { } else if (events[type] !== undefined) {
if (--this._eventsCount === 0) if (--this._eventsCount === 0) this._events = Object.create(null);
this._events = Object.create(null); else delete events[type];
else
delete events[type];
} }
return this; return this;
} }
@ -369,23 +392,22 @@ EventEmitter.prototype.removeAllListeners =
} }
return this; return this;
}; };
function _listeners(target, type, unwrap) { function _listeners(target, type, unwrap) {
var events = target._events; var events = target._events;
if (events === undefined) if (events === undefined) return [];
return [];
var evlistener = events[type]; var evlistener = events[type];
if (evlistener === undefined) if (evlistener === undefined) return [];
return [];
if (typeof evlistener === 'function') if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? return unwrap
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); ? unwrapListeners(evlistener)
: arrayClone(evlistener, evlistener.length);
} }
EventEmitter.prototype.listeners = function listeners(type) { EventEmitter.prototype.listeners = function listeners(type) {
@ -396,7 +418,7 @@ EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false); return _listeners(this, type, false);
}; };
EventEmitter.listenerCount = function(emitter, type) { EventEmitter.listenerCount = function (emitter, type) {
if (typeof emitter.listenerCount === 'function') { if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type); return emitter.listenerCount(type);
} else { } else {
@ -427,14 +449,12 @@ EventEmitter.prototype.eventNames = function eventNames() {
function arrayClone(arr, n) { function arrayClone(arr, n) {
var copy = new Array(n); var copy = new Array(n);
for (var i = 0; i < n; ++i) for (var i = 0; i < n; ++i) copy[i] = arr[i];
copy[i] = arr[i];
return copy; return copy;
} }
function spliceOne(list, index) { function spliceOne(list, index) {
for (; index + 1 < list.length; index++) for (; index + 1 < list.length; index++) list[index] = list[index + 1];
list[index] = list[index + 1];
list.pop(); list.pop();
} }
@ -458,11 +478,13 @@ function once(emitter, name) {
emitter.removeListener('error', errorListener); emitter.removeListener('error', errorListener);
} }
resolve([].slice.call(arguments)); resolve([].slice.call(arguments));
}; }
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== 'error') { if (name !== 'error') {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); addErrorHandlerIfEventEmitter(emitter, errorListener, {
once: true,
});
} }
}); });
} }
@ -492,6 +514,9 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
listener(arg); listener(arg);
}); });
} else { } else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); throw new TypeError(
'The "emitter" argument must be of type EventEmitter. Received type ' +
typeof emitter
);
} }
} }

View file

@ -1,5 +1,5 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import HookEvent from "./hook.js"; import HookEvent from './hook.js';
class History extends EventEmitter { class History extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -14,47 +14,73 @@ class History extends EventEmitter {
this.go = this.historyProto.go; this.go = this.historyProto.go;
this.back = this.historyProto.back; this.back = this.historyProto.back;
this.forward = this.historyProto.forward; this.forward = this.historyProto.forward;
}; }
override() { override() {
this.overridePushState(); this.overridePushState();
this.overrideReplaceState(); this.overrideReplaceState();
this.overrideGo(); this.overrideGo();
this.overrideForward(); this.overrideForward();
this.overrideBack(); this.overrideBack();
}; }
overridePushState() { overridePushState() {
this.ctx.override(this.historyProto, 'pushState', (target, that, args) => { this.ctx.override(
this.historyProto,
'pushState',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ state, title, url = '' ] = args; let [state, title, url = ''] = args;
const event = new HookEvent({ state, title, url }, target, that); const event = new HookEvent(
{ state, title, url },
target,
that
);
this.emit('pushState', event); this.emit('pushState', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.state, event.data.title, event.data.url); return event.target.call(
}); event.that,
}; event.data.state,
event.data.title,
event.data.url
);
}
);
}
overrideReplaceState() { overrideReplaceState() {
this.ctx.override(this.historyProto, 'replaceState', (target, that, args) => { this.ctx.override(
this.historyProto,
'replaceState',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ state, title, url = '' ] = args; let [state, title, url = ''] = args;
const event = new HookEvent({ state, title, url }, target, that); const event = new HookEvent(
{ state, title, url },
target,
that
);
this.emit('replaceState', event); this.emit('replaceState', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.state, event.data.title, event.data.url); return event.target.call(
}); event.that,
}; event.data.state,
event.data.title,
event.data.url
);
}
);
}
overrideGo() { overrideGo() {
this.ctx.override(this.historyProto, 'go', (target, that, [ delta ]) => { this.ctx.override(this.historyProto, 'go', (target, that, [delta]) => {
const event = new HookEvent({ delta }, target, that); const event = new HookEvent({ delta }, target, that);
this.emit('go', event); this.emit('go', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.delta); return event.target.call(event.that, event.data.delta);
}); });
}; }
overrideForward() { overrideForward() {
this.ctx.override(this.historyProto, 'forward', (target, that) => { this.ctx.override(this.historyProto, 'forward', (target, that) => {
const event = new HookEvent(null, target, that); const event = new HookEvent(null, target, that);
@ -63,7 +89,7 @@ class History extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that); return event.target.call(event.that);
}); });
}; }
overrideBack() { overrideBack() {
this.ctx.override(this.historyProto, 'back', (target, that) => { this.ctx.override(this.historyProto, 'back', (target, that) => {
const event = new HookEvent(null, target, that); const event = new HookEvent(null, target, that);
@ -72,7 +98,7 @@ class History extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that); return event.target.call(event.that);
}); });
}; }
}; }
export default History; export default History;

View file

@ -7,17 +7,17 @@ class HookEvent {
this.data = data; this.data = data;
this.target = target; this.target = target;
this.that = that; this.that = that;
}; }
get intercepted() { get intercepted() {
return this.#intercepted; return this.#intercepted;
}; }
get returnValue() { get returnValue() {
return this.#returnValue; return this.#returnValue;
}; }
respondWith(input) { respondWith(input) {
this.#returnValue = input; this.#returnValue = input;
this.#intercepted = true; this.#intercepted = true;
}; }
}; }
export default HookEvent; export default HookEvent;

View file

@ -1,22 +1,22 @@
import DocumentHook from "./dom/document.js"; import DocumentHook from './dom/document.js';
import ElementApi from "./dom/element.js"; import ElementApi from './dom/element.js';
import NodeApi from "./dom/node.js"; import NodeApi from './dom/node.js';
import AttrApi from "./dom/attr.js"; import AttrApi from './dom/attr.js';
import FunctionHook from "./native/function.js"; import FunctionHook from './native/function.js';
import ObjectHook from "./native/object.js"; import ObjectHook from './native/object.js';
import Fetch from "./requests/fetch.js"; import Fetch from './requests/fetch.js';
import WebSocketApi from "./requests/websocket.js"; import WebSocketApi from './requests/websocket.js';
import Xhr from "./requests/xhr.js"; import Xhr from './requests/xhr.js';
import EventSourceApi from "./requests/eventsource.js"; import EventSourceApi from './requests/eventsource.js';
import History from "./history.js"; import History from './history.js';
import LocationApi from "./location.js"; import LocationApi from './location.js';
import MessageApi from "./message.js"; import MessageApi from './message.js';
import NavigatorApi from "./navigator.js"; import NavigatorApi from './navigator.js';
import Workers from "./worker.js"; import Workers from './worker.js';
import URLApi from "./url.js"; import URLApi from './url.js';
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import StorageApi from "./storage.js"; import StorageApi from './storage.js';
import StyleApi from "./dom/style.js"; import StyleApi from './dom/style.js';
class UVClient extends EventEmitter { class UVClient extends EventEmitter {
constructor(window = self, worker = !window.window) { constructor(window = self, worker = !window.window) {
@ -25,8 +25,10 @@ class UVClient extends EventEmitter {
this.nativeMethods = { this.nativeMethods = {
fnToString: this.window.Function.prototype.toString, fnToString: this.window.Function.prototype.toString,
defineProperty: this.window.Object.defineProperty, defineProperty: this.window.Object.defineProperty,
getOwnPropertyDescriptor: this.window.Object.getOwnPropertyDescriptor, getOwnPropertyDescriptor:
getOwnPropertyDescriptors: this.window.Object.getOwnPropertyDescriptors, this.window.Object.getOwnPropertyDescriptor,
getOwnPropertyDescriptors:
this.window.Object.getOwnPropertyDescriptors,
getOwnPropertyNames: this.window.Object.getOwnPropertyNames, getOwnPropertyNames: this.window.Object.getOwnPropertyNames,
keys: this.window.Object.keys, keys: this.window.Object.keys,
getOwnPropertySymbols: this.window.Object.getOwnPropertySymbols, getOwnPropertySymbols: this.window.Object.getOwnPropertySymbols,
@ -41,7 +43,7 @@ class UVClient extends EventEmitter {
this.xhr = new Xhr(this); this.xhr = new Xhr(this);
this.history = new History(this); this.history = new History(this);
this.element = new ElementApi(this); this.element = new ElementApi(this);
this.node = new NodeApi(this) this.node = new NodeApi(this);
this.document = new DocumentHook(this); this.document = new DocumentHook(this);
this.function = new FunctionHook(this); this.function = new FunctionHook(this);
this.object = new ObjectHook(this); this.object = new ObjectHook(this);
@ -55,27 +57,35 @@ class UVClient extends EventEmitter {
this.location = new LocationApi(this); this.location = new LocationApi(this);
this.storage = new StorageApi(this); this.storage = new StorageApi(this);
this.style = new StyleApi(this); this.style = new StyleApi(this);
}; }
initLocation(rewriteUrl, sourceUrl) { initLocation(rewriteUrl, sourceUrl) {
this.location = new LocationApi(this, sourceUrl, rewriteUrl, this.worker); this.location = new LocationApi(
}; this,
sourceUrl,
rewriteUrl,
this.worker
);
}
override(obj, prop, wrapper, construct) { override(obj, prop, wrapper, construct) {
if (!prop in obj) return false; if (!prop in obj) return false;
const wrapped = this.wrap(obj, prop, wrapper, construct); const wrapped = this.wrap(obj, prop, wrapper, construct);
return obj[prop] = wrapped; return (obj[prop] = wrapped);
}; }
overrideDescriptor(obj, prop, wrapObj = {}) { overrideDescriptor(obj, prop, wrapObj = {}) {
const wrapped = this.wrapDescriptor(obj, prop, wrapObj); const wrapped = this.wrapDescriptor(obj, prop, wrapObj);
if (!wrapped) return {}; if (!wrapped) return {};
this.nativeMethods.defineProperty(obj, prop, wrapped); this.nativeMethods.defineProperty(obj, prop, wrapped);
return wrapped; return wrapped;
}; }
wrap(obj, prop, wrap, construct) { wrap(obj, prop, wrap, construct) {
const fn = obj[prop]; const fn = obj[prop];
if (!fn) return fn; if (!fn) return fn;
const wrapped = 'prototype' in fn ? function attach() { const wrapped =
'prototype' in fn
? function attach() {
return wrap(fn, this, [...arguments]); return wrap(fn, this, [...arguments]);
} : { }
: {
attach() { attach() {
return wrap(fn, this, [...arguments]); return wrap(fn, this, [...arguments]);
}, },
@ -84,27 +94,33 @@ class UVClient extends EventEmitter {
if (!!construct) { if (!!construct) {
wrapped.prototype = fn.prototype; wrapped.prototype = fn.prototype;
wrapped.prototype.constructor = wrapped; wrapped.prototype.constructor = wrapped;
}; }
this.emit('wrap', fn, wrapped, !!construct); this.emit('wrap', fn, wrapped, !!construct);
return wrapped; return wrapped;
}; }
wrapDescriptor(obj, prop, wrapObj = {}) { wrapDescriptor(obj, prop, wrapObj = {}) {
const descriptor = this.nativeMethods.getOwnPropertyDescriptor(obj, prop); const descriptor = this.nativeMethods.getOwnPropertyDescriptor(
obj,
prop
);
if (!descriptor) return false; if (!descriptor) return false;
for (let key in wrapObj) { for (let key in wrapObj) {
if (key in descriptor) { if (key in descriptor) {
if (key === 'get' || key === 'set') { if (key === 'get' || key === 'set') {
descriptor[key] = this.wrap(descriptor, key, wrapObj[key]); descriptor[key] = this.wrap(descriptor, key, wrapObj[key]);
} else { } else {
descriptor[key] = typeof wrapObj[key] == 'function' ? wrapObj[key](descriptor[key]) : wrapObj[key]; descriptor[key] =
}; typeof wrapObj[key] == 'function'
? wrapObj[key](descriptor[key])
: wrapObj[key];
}
}
} }
};
return descriptor; return descriptor;
}; }
}; }
export default UVClient; export default UVClient;
if (typeof self === 'object') self.UVClient = UVClient; if (typeof self === 'object') self.UVClient = UVClient;

View file

@ -1,4 +1,4 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
class LocationApi extends EventEmitter { class LocationApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -6,13 +6,31 @@ class LocationApi extends EventEmitter {
this.ctx = ctx; this.ctx = ctx;
this.window = ctx.window; this.window = ctx.window;
this.location = this.window.location; this.location = this.window.location;
this.WorkerLocation = this.ctx.worker ? this.window.WorkerLocation : null; this.WorkerLocation = this.ctx.worker
this.workerLocProto = this.WorkerLocation ? this.WorkerLocation.prototype : {}; ? this.window.WorkerLocation
this.keys = ['href', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash', 'origin']; : null;
this.workerLocProto = this.WorkerLocation
? this.WorkerLocation.prototype
: {};
this.keys = [
'href',
'protocol',
'host',
'hostname',
'port',
'pathname',
'search',
'hash',
'origin',
];
this.HashChangeEvent = this.window.HashChangeEvent || null; this.HashChangeEvent = this.window.HashChangeEvent || null;
this.href = this.WorkerLocation ? ctx.nativeMethods.getOwnPropertyDescriptor(this.workerLocProto, 'href') : this.href = this.WorkerLocation
ctx.nativeMethods.getOwnPropertyDescriptor(this.location, 'href'); ? ctx.nativeMethods.getOwnPropertyDescriptor(
}; this.workerLocProto,
'href'
)
: ctx.nativeMethods.getOwnPropertyDescriptor(this.location, 'href');
}
overrideWorkerLocation(parse) { overrideWorkerLocation(parse) {
if (!this.WorkerLocation) return false; if (!this.WorkerLocation) return false;
const uv = this; const uv = this;
@ -20,15 +38,13 @@ class LocationApi extends EventEmitter {
for (const key of this.keys) { for (const key of this.keys) {
this.ctx.overrideDescriptor(this.workerLocProto, key, { this.ctx.overrideDescriptor(this.workerLocProto, key, {
get: (target, that) => { get: (target, that) => {
return parse( return parse(uv.href.get.call(this.location))[key];
uv.href.get.call(this.location)
)[key]
}, },
}); });
}; }
return true; return true;
}; }
emulate(parse, wrap) { emulate(parse, wrap) {
const emulation = {}; const emulation = {};
const that = this; const that = this;
@ -36,81 +52,124 @@ class LocationApi extends EventEmitter {
for (const key of that.keys) { for (const key of that.keys) {
this.ctx.nativeMethods.defineProperty(emulation, key, { this.ctx.nativeMethods.defineProperty(emulation, key, {
get() { get() {
return parse( return parse(that.href.get.call(that.location))[key];
that.href.get.call(that.location)
)[key];
}, },
set: key !== 'origin' ? function (val) { set:
switch(key) { key !== 'origin'
? function (val) {
switch (key) {
case 'href': case 'href':
that.location.href = wrap(val); that.location.href = wrap(val);
break; break;
case 'hash': case 'hash':
that.emit('hashchange', emulation.href, (val.trim().startsWith('#') ? new URL(val.trim(), emulation.href).href : new URL('#' + val.trim(), emulation.href).href), that); that.emit(
'hashchange',
emulation.href,
val.trim().startsWith('#')
? new URL(
val.trim(),
emulation.href
).href
: new URL(
'#' + val.trim(),
emulation.href
).href,
that
);
break; break;
default: default:
const url = new URL(emulation.href); const url = new URL(emulation.href);
url[key] = val; url[key] = val;
that.location.href = wrap(url.href); that.location.href = wrap(url.href);
}; }
} : undefined, }
: undefined,
configurable: false, configurable: false,
enumerable: true, enumerable: true,
}); });
}; }
if ('reload' in this.location) { if ('reload' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'reload', { this.ctx.nativeMethods.defineProperty(emulation, 'reload', {
value: this.ctx.wrap(this.location, 'reload', (target, that) => { value: this.ctx.wrap(
return target.call(that === emulation ? this.location : that); this.location,
}), 'reload',
(target, that) => {
return target.call(
that === emulation ? this.location : that
);
}
),
writable: false, writable: false,
enumerable: true, enumerable: true,
}); });
}; }
if ('replace' in this.location) { if ('replace' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'replace', { this.ctx.nativeMethods.defineProperty(emulation, 'replace', {
value: this.ctx.wrap(this.location, 'assign', (target, that, args) => { value: this.ctx.wrap(
if (!args.length || that !== emulation) target.call(that); this.location,
'assign',
(target, that, args) => {
if (!args.length || that !== emulation)
target.call(that);
that = this.location; that = this.location;
let [ input ] = args; let [input] = args;
const url = new URL(input, emulation.href); const url = new URL(input, emulation.href);
return target.call(that === emulation ? this.location : that, wrap(url.href)); return target.call(
}), that === emulation ? this.location : that,
wrap(url.href)
);
}
),
writable: false, writable: false,
enumerable: true, enumerable: true,
}); });
}; }
if ('assign' in this.location) { if ('assign' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'assign', { this.ctx.nativeMethods.defineProperty(emulation, 'assign', {
value: this.ctx.wrap(this.location, 'assign', (target, that, args) => { value: this.ctx.wrap(
if (!args.length || that !== emulation) target.call(that); this.location,
'assign',
(target, that, args) => {
if (!args.length || that !== emulation)
target.call(that);
that = this.location; that = this.location;
let [ input ] = args; let [input] = args;
const url = new URL(input, emulation.href); const url = new URL(input, emulation.href);
return target.call(that === emulation ? this.location : that, wrap(url.href)); return target.call(
}), that === emulation ? this.location : that,
wrap(url.href)
);
}
),
writable: false, writable: false,
enumerable: true, enumerable: true,
}); });
}; }
if ('ancestorOrigins' in this.location) { if ('ancestorOrigins' in this.location) {
this.ctx.nativeMethods.defineProperty(emulation, 'ancestorOrigins', { this.ctx.nativeMethods.defineProperty(
emulation,
'ancestorOrigins',
{
get() { get() {
const arr = []; const arr = [];
if (that.window.DOMStringList) that.ctx.nativeMethods.setPrototypeOf(arr, that.window.DOMStringList.prototype); if (that.window.DOMStringList)
that.ctx.nativeMethods.setPrototypeOf(
arr,
that.window.DOMStringList.prototype
);
return arr; return arr;
}, },
set: undefined, set: undefined,
enumerable: true, enumerable: true,
}); }
}; );
}
this.ctx.nativeMethods.defineProperty(emulation, 'toString', { this.ctx.nativeMethods.defineProperty(emulation, 'toString', {
value: this.ctx.wrap(this.location, 'toString', () => { value: this.ctx.wrap(this.location, 'toString', () => {
@ -126,10 +185,14 @@ class LocationApi extends EventEmitter {
enumerable: false, enumerable: false,
}); });
if (this.ctx.window.Location) this.ctx.nativeMethods.setPrototypeOf(emulation, this.ctx.window.Location.prototype); if (this.ctx.window.Location)
this.ctx.nativeMethods.setPrototypeOf(
emulation,
this.ctx.window.Location.prototype
);
return emulation; return emulation;
}; }
}; }
export default LocationApi; export default LocationApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import HookEvent from "./hook.js"; import HookEvent from './hook.js';
class MessageApi extends EventEmitter { class MessageApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -12,9 +12,15 @@ class MessageApi extends EventEmitter {
this.mpProto = this.MessagePort.prototype || {}; this.mpProto = this.MessagePort.prototype || {};
this.mpPostMessage = this.mpProto.postMessage; this.mpPostMessage = this.mpProto.postMessage;
this.messageProto = this.MessageEvent.prototype || {}; this.messageProto = this.MessageEvent.prototype || {};
this.messageData = ctx.nativeMethods.getOwnPropertyDescriptor(this.messageProto, 'data'); this.messageData = ctx.nativeMethods.getOwnPropertyDescriptor(
this.messageOrigin = ctx.nativeMethods.getOwnPropertyDescriptor(this.messageProto, 'origin'); this.messageProto,
}; 'data'
);
this.messageOrigin = ctx.nativeMethods.getOwnPropertyDescriptor(
this.messageProto,
'origin'
);
}
overridePostMessage() { overridePostMessage() {
this.ctx.override(this.window, 'postMessage', (target, that, args) => { this.ctx.override(this.window, 'postMessage', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
@ -24,61 +30,100 @@ class MessageApi extends EventEmitter {
let transfer; let transfer;
if (!this.ctx.worker) { if (!this.ctx.worker) {
[ message, origin, transfer = [] ] = args; [message, origin, transfer = []] = args;
} else { } else {
[ message, transfer = [] ] = args; [message, transfer = []] = args;
}; }
const event = new HookEvent({ message, origin, transfer, worker: this.ctx.worker }, target, that); const event = new HookEvent(
{ message, origin, transfer, worker: this.ctx.worker },
target,
that
);
this.emit('postMessage', event); this.emit('postMessage', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return this.ctx.worker ? event.target.call(event.that, event.data.message, event.data.transfer) : event.target.call(event.that, event.data.message, event.data.origin, event.data.transfer); return this.ctx.worker
? event.target.call(
event.that,
event.data.message,
event.data.transfer
)
: event.target.call(
event.that,
event.data.message,
event.data.origin,
event.data.transfer
);
}); });
}; }
wrapPostMessage(obj, prop, noOrigin = false) { wrapPostMessage(obj, prop, noOrigin = false) {
return this.ctx.wrap(obj, prop, (target, that, args) => { return this.ctx.wrap(obj, prop, (target, that, args) => {
if (this.ctx.worker ? !args.length : 2 > args) return target.apply(that, args); if (this.ctx.worker ? !args.length : 2 > args)
return target.apply(that, args);
let message; let message;
let origin; let origin;
let transfer; let transfer;
if (!noOrigin) { if (!noOrigin) {
[ message, origin, transfer = [] ] = args; [message, origin, transfer = []] = args;
} else { } else {
[ message, transfer = [] ] = args; [message, transfer = []] = args;
origin = null; origin = null;
}; }
const event = new HookEvent({ message, origin, transfer, worker: this.ctx.worker }, target, obj); const event = new HookEvent(
{ message, origin, transfer, worker: this.ctx.worker },
target,
obj
);
this.emit('postMessage', event); this.emit('postMessage', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return noOrigin ? event.target.call(event.that, event.data.message, event.data.transfer) : event.target.call(event.that, event.data.message, event.data.origin, event.data.transfer); return noOrigin
? event.target.call(
event.that,
event.data.message,
event.data.transfer
)
: event.target.call(
event.that,
event.data.message,
event.data.origin,
event.data.transfer
);
}); });
}; }
overrideMessageOrigin() { overrideMessageOrigin() {
this.ctx.overrideDescriptor(this.messageProto, 'origin', { this.ctx.overrideDescriptor(this.messageProto, 'origin', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('origin', event); this.emit('origin', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
} },
}); });
}; }
overrideMessageData() { overrideMessageData() {
this.ctx.overrideDescriptor(this.messageProto, 'data', { this.ctx.overrideDescriptor(this.messageProto, 'data', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('data', event); this.emit('data', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
} },
}); });
}; }
}; }
export default MessageApi; export default MessageApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class FunctionHook extends EventEmitter { class FunctionHook extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -13,9 +13,12 @@ class FunctionHook extends EventEmitter {
this.call = this.fnProto.call; this.call = this.fnProto.call;
this.apply = this.fnProto.apply; this.apply = this.fnProto.apply;
this.bind = this.fnProto.bind; this.bind = this.fnProto.bind;
}; }
overrideFunction() { overrideFunction() {
this.ctx.override(this.window, 'Function', (target, that, args) => { this.ctx.override(
this.window,
'Function',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let script = args[args.length - 1]; let script = args[args.length - 1];
@ -23,15 +26,25 @@ class FunctionHook extends EventEmitter {
for (let i = 0; i < args.length - 1; i++) { for (let i = 0; i < args.length - 1; i++) {
fnArgs.push(args[i]); fnArgs.push(args[i]);
}; }
const event = new HookEvent({ script, args: fnArgs }, target, that); const event = new HookEvent(
{ script, args: fnArgs },
target,
that
);
this.emit('function', event); this.emit('function', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, ...event.data.args, event.data.script); return event.target.call(
}, true); event.that,
}; ...event.data.args,
event.data.script
);
},
true
);
}
overrideToString() { overrideToString() {
this.ctx.override(this.fnProto, 'toString', (target, that) => { this.ctx.override(this.fnProto, 'toString', (target, that) => {
const event = new HookEvent({ fn: that }, target, that); const event = new HookEvent({ fn: that }, target, that);
@ -40,7 +53,7 @@ class FunctionHook extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.data.fn); return event.target.call(event.data.fn);
}); });
}; }
}; }
export default FunctionHook; export default FunctionHook;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class ObjectHook extends EventEmitter { class ObjectHook extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -10,31 +10,47 @@ class ObjectHook extends EventEmitter {
this.getOwnPropertyDescriptors = this.Object.getOwnPropertyDescriptors; this.getOwnPropertyDescriptors = this.Object.getOwnPropertyDescriptors;
this.getOwnPropertyDescriptor = this.Object.getOwnPropertyDescriptor; this.getOwnPropertyDescriptor = this.Object.getOwnPropertyDescriptor;
this.getOwnPropertyNames = this.Object.getOwnPropertyNames; this.getOwnPropertyNames = this.Object.getOwnPropertyNames;
}; }
overrideGetPropertyNames() { overrideGetPropertyNames() {
this.ctx.override(this.Object, 'getOwnPropertyNames', (target, that, args) => { this.ctx.override(
this.Object,
'getOwnPropertyNames',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ object ] = args; let [object] = args;
const event = new HookEvent({ names: target.call(that, object) }, target, that); const event = new HookEvent(
{ names: target.call(that, object) },
target,
that
);
this.emit('getOwnPropertyNames', event); this.emit('getOwnPropertyNames', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.names; return event.data.names;
}); }
}; );
}
overrideGetOwnPropertyDescriptors() { overrideGetOwnPropertyDescriptors() {
this.ctx.override(this.Object, 'getOwnPropertyDescriptors', (target, that, args) => { this.ctx.override(
this.Object,
'getOwnPropertyDescriptors',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ object ] = args; let [object] = args;
const event = new HookEvent({ descriptors: target.call(that, object) }, target, that); const event = new HookEvent(
{ descriptors: target.call(that, object) },
target,
that
);
this.emit('getOwnPropertyDescriptors', event); this.emit('getOwnPropertyDescriptors', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.descriptors; return event.data.descriptors;
}); }
}; );
}; }
}
export default ObjectHook; export default ObjectHook;

View file

@ -1,5 +1,5 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import HookEvent from "./hook.js"; import HookEvent from './hook.js';
class NavigatorApi extends EventEmitter { class NavigatorApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -10,19 +10,23 @@ class NavigatorApi extends EventEmitter {
this.Navigator = this.window.Navigator || {}; this.Navigator = this.window.Navigator || {};
this.navProto = this.Navigator.prototype || {}; this.navProto = this.Navigator.prototype || {};
this.sendBeacon = this.navProto.sendBeacon; this.sendBeacon = this.navProto.sendBeacon;
}; }
overrideSendBeacon() { overrideSendBeacon() {
this.ctx.override(this.navProto, 'sendBeacon', (target, that, args) => { this.ctx.override(this.navProto, 'sendBeacon', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ url, data = '' ] = args; let [url, data = ''] = args;
const event = new HookEvent({ url, data }, target, that); const event = new HookEvent({ url, data }, target, that);
this.emit('sendBeacon', event); this.emit('sendBeacon', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.url, event.data.data); return event.target.call(
event.that,
event.data.url,
event.data.data
);
}); });
}; }
}; }
export default NavigatorApi; export default NavigatorApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class EventSourceApi extends EventEmitter { class EventSourceApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -8,38 +8,50 @@ class EventSourceApi extends EventEmitter {
this.window = ctx.window; this.window = ctx.window;
this.EventSource = this.window.EventSource || {}; this.EventSource = this.window.EventSource || {};
this.esProto = this.EventSource.prototype || {}; this.esProto = this.EventSource.prototype || {};
this.url = ctx.nativeMethods.getOwnPropertyDescriptor(this.esProto, 'url'); this.url = ctx.nativeMethods.getOwnPropertyDescriptor(
this.esProto,
'url'
);
this.CONNECTING = 0; this.CONNECTING = 0;
this.OPEN = 1; this.OPEN = 1;
this.CLOSED = 2; this.CLOSED = 2;
}; }
overrideConstruct() { overrideConstruct() {
this.ctx.override(this.window, 'EventSource', (target, that, args) => { this.ctx.override(
this.window,
'EventSource',
(target, that, args) => {
if (!args.length) return new target(...args); if (!args.length) return new target(...args);
let [ url, config = {} ] = args; let [url, config = {}] = args;
const event = new HookEvent({ url, config }, target, that); const event = new HookEvent({ url, config }, target, that);
this.emit('construct', event); this.emit('construct', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return new event.target(event.data.url, event.data.config); return new event.target(event.data.url, event.data.config);
}, true); },
true
);
if ('EventSource' in this.window) { if ('EventSource' in this.window) {
this.window.EventSource.CONNECTING = this.CONNECTING; this.window.EventSource.CONNECTING = this.CONNECTING;
this.window.EventSource.OPEN = this.OPEN; this.window.EventSource.OPEN = this.OPEN;
this.window.EventSource.CLOSED = this.CLOSED; this.window.EventSource.CLOSED = this.CLOSED;
}; }
}; }
overrideUrl() { overrideUrl() {
this.ctx.overrideDescriptor(this.esProto, 'url', { this.ctx.overrideDescriptor(this.esProto, 'url', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('url', event); this.emit('url', event);
return event.data.value; return event.data.value;
}, },
}); });
}; }
}; }
export default EventSourceApi; export default EventSourceApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class Fetch extends EventEmitter { class Fetch extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -13,49 +13,75 @@ class Fetch extends EventEmitter {
this.reqProto = this.Request ? this.Request.prototype : {}; this.reqProto = this.Request ? this.Request.prototype : {};
this.resProto = this.Response ? this.Response.prototype : {}; this.resProto = this.Response ? this.Response.prototype : {};
this.headersProto = this.Headers ? this.Headers.prototype : {}; this.headersProto = this.Headers ? this.Headers.prototype : {};
this.reqUrl = ctx.nativeMethods.getOwnPropertyDescriptor(this.reqProto, 'url'); this.reqUrl = ctx.nativeMethods.getOwnPropertyDescriptor(
this.resUrl = ctx.nativeMethods.getOwnPropertyDescriptor(this.resProto, 'url'); this.reqProto,
this.reqHeaders = ctx.nativeMethods.getOwnPropertyDescriptor(this.reqProto, 'headers'); 'url'
this.resHeaders = ctx.nativeMethods.getOwnPropertyDescriptor(this.resProto, 'headers'); );
}; this.resUrl = ctx.nativeMethods.getOwnPropertyDescriptor(
this.resProto,
'url'
);
this.reqHeaders = ctx.nativeMethods.getOwnPropertyDescriptor(
this.reqProto,
'headers'
);
this.resHeaders = ctx.nativeMethods.getOwnPropertyDescriptor(
this.resProto,
'headers'
);
}
override() { override() {
this.overrideRequest(); this.overrideRequest();
this.overrideUrl(); this.overrideUrl();
this.overrideHeaders(); this.overrideHeaders();
return true; return true;
}; }
overrideRequest() { overrideRequest() {
if (!this.fetch) return false; if (!this.fetch) return false;
this.ctx.override(this.window, 'fetch', (target, that, args) => { this.ctx.override(this.window, 'fetch', (target, that, args) => {
if (!args.length || args[0] instanceof this.Request) return target.apply(that, args); if (!args.length || args[0] instanceof this.Request)
return target.apply(that, args);
let [ input, options = {} ] = args; let [input, options = {}] = args;
const event = new HookEvent({ input, options }, target, that); const event = new HookEvent({ input, options }, target, that);
this.emit('request', event); this.emit('request', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.input, event.data.options); return event.target.call(
event.that,
event.data.input,
event.data.options
);
}); });
this.ctx.override(this.window, 'Request', (target, that, args) => { this.ctx.override(
this.window,
'Request',
(target, that, args) => {
if (!args.length) return new target(...args); if (!args.length) return new target(...args);
let [ input, options = {} ] = args; let [input, options = {}] = args;
const event = new HookEvent({ input, options }, target); const event = new HookEvent({ input, options }, target);
this.emit('request', event); this.emit('request', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return new event.target(event.data.input, event.data.options); return new event.target(event.data.input, event.data.options);
}, true); },
true
);
return true; return true;
}; }
overrideUrl() { overrideUrl() {
this.ctx.overrideDescriptor(this.reqProto, 'url', { this.ctx.overrideDescriptor(this.reqProto, 'url', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('requestUrl', event); this.emit('requestUrl', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -66,7 +92,11 @@ class Fetch extends EventEmitter {
this.ctx.overrideDescriptor(this.resProto, 'url', { this.ctx.overrideDescriptor(this.resProto, 'url', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('responseUrl', event); this.emit('responseUrl', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -75,13 +105,17 @@ class Fetch extends EventEmitter {
}, },
}); });
return true; return true;
}; }
overrideHeaders() { overrideHeaders() {
if (!this.Headers) return false; if (!this.Headers) return false;
this.ctx.overrideDescriptor(this.reqProto, 'headers', { this.ctx.overrideDescriptor(this.reqProto, 'headers', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('requestHeaders', event); this.emit('requestHeaders', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -92,7 +126,11 @@ class Fetch extends EventEmitter {
this.ctx.overrideDescriptor(this.resProto, 'headers', { this.ctx.overrideDescriptor(this.resProto, 'headers', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('responseHeaders', event); this.emit('responseHeaders', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -101,9 +139,13 @@ class Fetch extends EventEmitter {
}, },
}); });
this.ctx.override(this.headersProto, 'get', (target, that, [ name ]) => { this.ctx.override(this.headersProto, 'get', (target, that, [name]) => {
if (!name) return target.call(that); if (!name) return target.call(that);
const event = new HookEvent({ name, value: target.call(that, name) }, target, that); const event = new HookEvent(
{ name, value: target.call(that, name) },
target,
that
);
this.emit('getHeader', event); this.emit('getHeader', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -114,20 +156,28 @@ class Fetch extends EventEmitter {
this.ctx.override(this.headersProto, 'set', (target, that, args) => { this.ctx.override(this.headersProto, 'set', (target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args; let [name, value] = args;
const event = new HookEvent({ name, value }, target, that); const event = new HookEvent({ name, value }, target, that);
this.emit('setHeader', event); this.emit('setHeader', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value); return event.target.call(
event.that,
event.data.name,
event.data.value
);
}); });
this.ctx.override(this.headersProto, 'has', (target, that, args) => { this.ctx.override(this.headersProto, 'has', (target, that, args) => {
if (!args.length) return target.call(that); if (!args.length) return target.call(that);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name, value: target.call(that, name) }, target, that); const event = new HookEvent(
{ name, value: target.call(that, name) },
target,
that
);
this.emit('hasHeader', event); this.emit('hasHeader', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -138,19 +188,23 @@ class Fetch extends EventEmitter {
this.ctx.override(this.headersProto, 'append', (target, that, args) => { this.ctx.override(this.headersProto, 'append', (target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args; let [name, value] = args;
const event = new HookEvent({ name, value }, target, that); const event = new HookEvent({ name, value }, target, that);
this.emit('appendHeader', event); this.emit('appendHeader', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value); return event.target.call(
event.that,
event.data.name,
event.data.value
);
}); });
this.ctx.override(this.headersProto, 'delete', (target, that, args) => { this.ctx.override(this.headersProto, 'delete', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name }, target, that); const event = new HookEvent({ name }, target, that);
this.emit('deleteHeader', event); this.emit('deleteHeader', event);
@ -160,7 +214,7 @@ class Fetch extends EventEmitter {
}); });
return true; return true;
}; }
}; }
export default Fetch; export default Fetch;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class WebSocketApi extends EventEmitter { class WebSocketApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -8,51 +8,71 @@ class WebSocketApi extends EventEmitter {
this.window = ctx.window; this.window = ctx.window;
this.WebSocket = this.window.WebSocket || {}; this.WebSocket = this.window.WebSocket || {};
this.wsProto = this.WebSocket.prototype || {}; this.wsProto = this.WebSocket.prototype || {};
this.url = ctx.nativeMethods.getOwnPropertyDescriptor(this.wsProto, 'url'); this.url = ctx.nativeMethods.getOwnPropertyDescriptor(
this.protocol = ctx.nativeMethods.getOwnPropertyDescriptor(this.wsProto, 'protocol'); this.wsProto,
'url'
);
this.protocol = ctx.nativeMethods.getOwnPropertyDescriptor(
this.wsProto,
'protocol'
);
this.send = this.wsProto.send; this.send = this.wsProto.send;
this.close = this.wsProto.close; this.close = this.wsProto.close;
this.CONNECTING = 0; this.CONNECTING = 0;
this.OPEN = 1; this.OPEN = 1;
this.CLOSING = 2; this.CLOSING = 2;
this.CLOSED = 3; this.CLOSED = 3;
}; }
overrideWebSocket() { overrideWebSocket() {
this.ctx.override(this.window, 'WebSocket', (target, that, args) => { this.ctx.override(
this.window,
'WebSocket',
(target, that, args) => {
if (!args.length) return new target(...args); if (!args.length) return new target(...args);
let [ url, protocols = [] ] = args; let [url, protocols = []] = args;
if (!this.ctx.nativeMethods.isArray(protocols)) protocols = [ protocols ]; if (!this.ctx.nativeMethods.isArray(protocols))
protocols = [protocols];
const event = new HookEvent({ url, protocols }, target, that); const event = new HookEvent({ url, protocols }, target, that);
this.emit('websocket', event); this.emit('websocket', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return new event.target(event.data.url, event.data.protocols); return new event.target(event.data.url, event.data.protocols);
}, true); },
true
);
this.window.WebSocket.CONNECTING = this.CONNECTING; this.window.WebSocket.CONNECTING = this.CONNECTING;
this.window.WebSocket.OPEN = this.OPEN; this.window.WebSocket.OPEN = this.OPEN;
this.window.WebSocket.CLOSING = this.CLOSING; this.window.WebSocket.CLOSING = this.CLOSING;
this.window.WebSocket.CLOSED = this.CLOSED; this.window.WebSocket.CLOSED = this.CLOSED;
}; }
overrideUrl() { overrideUrl() {
this.ctx.overrideDescriptor(this.wsProto, 'url', { this.ctx.overrideDescriptor(this.wsProto, 'url', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('url', event); this.emit('url', event);
return event.data.value; return event.data.value;
}, },
}); });
}; }
overrideProtocol() { overrideProtocol() {
this.ctx.overrideDescriptor(this.wsProto, 'protocol', { this.ctx.overrideDescriptor(this.wsProto, 'protocol', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('protocol', event); this.emit('protocol', event);
return event.data.value; return event.data.value;
}, },
}); });
}; }
}; }
export default WebSocketApi; export default WebSocketApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "../events.js"; import EventEmitter from '../events.js';
import HookEvent from "../hook.js"; import HookEvent from '../hook.js';
class Xhr extends EventEmitter { class Xhr extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -7,17 +7,25 @@ class Xhr extends EventEmitter {
this.ctx = ctx; this.ctx = ctx;
this.window = ctx.window; this.window = ctx.window;
this.XMLHttpRequest = this.window.XMLHttpRequest; this.XMLHttpRequest = this.window.XMLHttpRequest;
this.xhrProto = this.window.XMLHttpRequest ? this.window.XMLHttpRequest.prototype : {}; this.xhrProto = this.window.XMLHttpRequest
? this.window.XMLHttpRequest.prototype
: {};
this.open = this.xhrProto.open; this.open = this.xhrProto.open;
this.abort = this.xhrProto.abort; this.abort = this.xhrProto.abort;
this.send = this.xhrProto.send; this.send = this.xhrProto.send;
this.overrideMimeType = this.xhrProto.overrideMimeType this.overrideMimeType = this.xhrProto.overrideMimeType;
this.getAllResponseHeaders = this.xhrProto.getAllResponseHeaders; this.getAllResponseHeaders = this.xhrProto.getAllResponseHeaders;
this.getResponseHeader = this.xhrProto.getResponseHeader; this.getResponseHeader = this.xhrProto.getResponseHeader;
this.setRequestHeader = this.xhrProto.setRequestHeader; this.setRequestHeader = this.xhrProto.setRequestHeader;
this.responseURL = ctx.nativeMethods.getOwnPropertyDescriptor(this.xhrProto, 'responseURL'); this.responseURL = ctx.nativeMethods.getOwnPropertyDescriptor(
this.responseText = ctx.nativeMethods.getOwnPropertyDescriptor(this.xhrProto, 'responseText'); this.xhrProto,
}; 'responseURL'
);
this.responseText = ctx.nativeMethods.getOwnPropertyDescriptor(
this.xhrProto,
'responseText'
);
}
override() { override() {
this.overrideOpen(); this.overrideOpen();
this.overrideSend(); this.overrideSend();
@ -25,13 +33,18 @@ class Xhr extends EventEmitter {
this.overrideGetResHeader(); this.overrideGetResHeader();
this.overrideGetResHeaders(); this.overrideGetResHeaders();
this.overrideSetReqHeader(); this.overrideSetReqHeader();
}; }
overrideOpen() { overrideOpen() {
this.ctx.override(this.xhrProto, 'open', (target, that, args) => { this.ctx.override(this.xhrProto, 'open', (target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ method, input, async = true, user = null, password = null ] = args; let [method, input, async = true, user = null, password = null] =
const event = new HookEvent({ method, input, async, user, password }, target, that); args;
const event = new HookEvent(
{ method, input, async, user, password },
target,
that
);
this.emit('open', event); this.emit('open', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
@ -45,65 +58,94 @@ class Xhr extends EventEmitter {
event.data.password event.data.password
); );
}); });
}; }
overrideResponseUrl() { overrideResponseUrl() {
this.ctx.overrideDescriptor(this.xhrProto, 'responseURL', { this.ctx.overrideDescriptor(this.xhrProto, 'responseURL', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that); const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('responseUrl', event); this.emit('responseUrl', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}, },
}); });
}; }
overrideSend() { overrideSend() {
this.ctx.override(this.xhrProto, 'send', (target, that, [ body = null ]) => { this.ctx.override(
this.xhrProto,
'send',
(target, that, [body = null]) => {
const event = new HookEvent({ body }, target, that); const event = new HookEvent({ body }, target, that);
this.emit('send', event); this.emit('send', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call( return event.target.call(event.that, event.data.body);
event.that, }
event.data.body,
); );
}); }
};
overrideSetReqHeader() { overrideSetReqHeader() {
this.ctx.override(this.xhrProto, 'setRequestHeader', (target, that, args) => { this.ctx.override(
this.xhrProto,
'setRequestHeader',
(target, that, args) => {
if (2 > args.length) return target.apply(that, args); if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args; let [name, value] = args;
const event = new HookEvent({ name, value }, target, that); const event = new HookEvent({ name, value }, target, that);
this.emit('setReqHeader', event); this.emit('setReqHeader', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value); return event.target.call(
}); event.that,
}; event.data.name,
event.data.value
);
}
);
}
overrideGetResHeaders() { overrideGetResHeaders() {
this.ctx.override(this.xhrProto, 'getAllResponseHeaders', (target, that) => { this.ctx.override(
const event = new HookEvent({ value: target.call(that) }, target, that); this.xhrProto,
'getAllResponseHeaders',
(target, that) => {
const event = new HookEvent(
{ value: target.call(that) },
target,
that
);
this.emit('getAllResponseHeaders', event); this.emit('getAllResponseHeaders', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}); }
}; );
}
overrideGetResHeader() { overrideGetResHeader() {
this.ctx.override(this.xhrProto, 'getResponseHeader', (target, that, args) => { this.ctx.override(
this.xhrProto,
'getResponseHeader',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ name ] = args; let [name] = args;
const event = new HookEvent({ name, value: target.call(that, name) }, target, that); const event = new HookEvent(
{ name, value: target.call(that, name) },
target,
that
);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.value; return event.data.value;
}); }
}; );
}; }
}
export default Xhr export default Xhr;

View file

@ -1,5 +1,5 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import HookEvent from "./hook.js"; import HookEvent from './hook.js';
class StorageApi extends EventEmitter { class StorageApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -17,73 +17,110 @@ class StorageApi extends EventEmitter {
this.key = this.storeProto.key || null; this.key = this.storeProto.key || null;
this.methods = ['key', 'getItem', 'setItem', 'removeItem', 'clear']; this.methods = ['key', 'getItem', 'setItem', 'removeItem', 'clear'];
this.wrappers = new ctx.nativeMethods.Map(); this.wrappers = new ctx.nativeMethods.Map();
}; }
overrideMethods() { overrideMethods() {
this.ctx.override(this.storeProto, 'getItem', (target, that, args) => { this.ctx.override(this.storeProto, 'getItem', (target, that, args) => {
if (!args.length) return target.apply((this.wrappers.get(that) || that), args); if (!args.length)
let [ name ] = args; return target.apply(this.wrappers.get(that) || that, args);
let [name] = args;
const event = new HookEvent({ name }, target, (this.wrappers.get(that) || that)); const event = new HookEvent(
{ name },
target,
this.wrappers.get(that) || that
);
this.emit('getItem', event); this.emit('getItem', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); });
this.ctx.override(this.storeProto, 'setItem', (target, that, args) => { this.ctx.override(this.storeProto, 'setItem', (target, that, args) => {
if (2 > args.length) return target.apply((this.wrappers.get(that) || that), args); if (2 > args.length)
let [ name, value ] = args; return target.apply(this.wrappers.get(that) || that, args);
let [name, value] = args;
const event = new HookEvent({ name, value }, target, (this.wrappers.get(that) || that)); const event = new HookEvent(
{ name, value },
target,
this.wrappers.get(that) || that
);
this.emit('setItem', event); this.emit('setItem', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value); return event.target.call(
event.that,
event.data.name,
event.data.value
);
}); });
this.ctx.override(this.storeProto, 'removeItem', (target, that, args) => { this.ctx.override(
if (!args.length) return target.apply((this.wrappers.get(that) || that), args); this.storeProto,
let [ name ] = args; 'removeItem',
(target, that, args) => {
if (!args.length)
return target.apply(this.wrappers.get(that) || that, args);
let [name] = args;
const event = new HookEvent({ name }, target, (this.wrappers.get(that) || that)); const event = new HookEvent(
{ name },
target,
this.wrappers.get(that) || that
);
this.emit('removeItem', event); this.emit('removeItem', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name); return event.target.call(event.that, event.data.name);
}); }
);
this.ctx.override(this.storeProto, 'clear', (target, that) => { this.ctx.override(this.storeProto, 'clear', (target, that) => {
const event = new HookEvent(null, target, (this.wrappers.get(that) || that)); const event = new HookEvent(
null,
target,
this.wrappers.get(that) || that
);
this.emit('clear', event); this.emit('clear', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that); return event.target.call(event.that);
}); });
this.ctx.override(this.storeProto, 'key', (target, that, args) => { this.ctx.override(this.storeProto, 'key', (target, that, args) => {
if (!args.length) return target.apply((this.wrappers.get(that) || that), args); if (!args.length)
let [ index ] = args; return target.apply(this.wrappers.get(that) || that, args);
let [index] = args;
const event = new HookEvent({ index }, target, (this.wrappers.get(that) || that)); const event = new HookEvent(
{ index },
target,
this.wrappers.get(that) || that
);
this.emit('key', event); this.emit('key', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.index); return event.target.call(event.that, event.data.index);
}); });
}; }
overrideLength() { overrideLength() {
this.ctx.overrideDescriptor(this.storeProto, 'length', { this.ctx.overrideDescriptor(this.storeProto, 'length', {
get: (target, that) => { get: (target, that) => {
const event = new HookEvent({ length: target.call((this.wrappers.get(that) || that)) }, target, (this.wrappers.get(that) || that)); const event = new HookEvent(
{ length: target.call(this.wrappers.get(that) || that) },
target,
this.wrappers.get(that) || that
);
this.emit('length', event); this.emit('length', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.data.length; return event.data.length;
}, },
}); });
}; }
emulate(storage, obj = {}) { emulate(storage, obj = {}) {
this.ctx.nativeMethods.setPrototypeOf(obj, this.storeProto); this.ctx.nativeMethods.setPrototypeOf(obj, this.storeProto);
const proxy = new this.ctx.window.Proxy(obj, { const proxy = new this.ctx.window.Proxy(obj, {
get: (target, prop) => { get: (target, prop) => {
if (prop in this.storeProto || typeof prop === 'symbol') return storage[prop]; if (prop in this.storeProto || typeof prop === 'symbol')
return storage[prop];
const event = new HookEvent({ name: prop }, null, storage); const event = new HookEvent({ name: prop }, null, storage);
this.emit('get', event); this.emit('get', event);
@ -92,14 +129,19 @@ class StorageApi extends EventEmitter {
return storage[event.data.name]; return storage[event.data.name];
}, },
set: (target, prop, value) => { set: (target, prop, value) => {
if (prop in this.storeProto || typeof prop === 'symbol') return storage[prop] = value; if (prop in this.storeProto || typeof prop === 'symbol')
return (storage[prop] = value);
const event = new HookEvent({ name: prop, value }, null, storage); const event = new HookEvent(
{ name: prop, value },
null,
storage
);
this.emit('set', event); this.emit('set', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return storage[event.data.name] = event.data.value; return (storage[event.data.name] = event.data.value);
}, },
deleteProperty: (target, prop) => { deleteProperty: (target, prop) => {
if (typeof prop === 'symbol') return delete storage[prop]; if (typeof prop === 'symbol') return delete storage[prop];
@ -117,8 +159,7 @@ class StorageApi extends EventEmitter {
this.ctx.nativeMethods.setPrototypeOf(proxy, this.storeProto); this.ctx.nativeMethods.setPrototypeOf(proxy, this.storeProto);
return proxy; return proxy;
}; }
}
};
export default StorageApi; export default StorageApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import HookEvent from "./hook.js"; import HookEvent from './hook.js';
class URLApi extends EventEmitter { class URLApi extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -9,11 +9,11 @@ class URLApi extends EventEmitter {
this.URL = this.window.URL || {}; this.URL = this.window.URL || {};
this.createObjectURL = this.URL.createObjectURL; this.createObjectURL = this.URL.createObjectURL;
this.revokeObjectURL = this.URL.revokeObjectURL; this.revokeObjectURL = this.URL.revokeObjectURL;
}; }
overrideObjectURL() { overrideObjectURL() {
this.ctx.override(this.URL, 'createObjectURL', (target, that, args) => { this.ctx.override(this.URL, 'createObjectURL', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ object ] = args; let [object] = args;
const event = new HookEvent({ object }, target, that); const event = new HookEvent({ object }, target, that);
this.emit('createObjectURL', event); this.emit('createObjectURL', event);
@ -23,7 +23,7 @@ class URLApi extends EventEmitter {
}); });
this.ctx.override(this.URL, 'revokeObjectURL', (target, that, args) => { this.ctx.override(this.URL, 'revokeObjectURL', (target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ url ] = args; let [url] = args;
const event = new HookEvent({ url }, target, that); const event = new HookEvent({ url }, target, that);
this.emit('revokeObjectURL', event); this.emit('revokeObjectURL', event);
@ -31,7 +31,7 @@ class URLApi extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.url); return event.target.call(event.that, event.data.url);
}); });
}; }
}; }
export default URLApi; export default URLApi;

View file

@ -1,5 +1,5 @@
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import HookEvent from "./hook.js"; import HookEvent from './hook.js';
class Workers extends EventEmitter { class Workers extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -13,45 +13,75 @@ class Workers extends EventEmitter {
this.postMessage = this.workerProto.postMessage; this.postMessage = this.workerProto.postMessage;
this.terminate = this.workerProto.terminate; this.terminate = this.workerProto.terminate;
this.addModule = this.workletProto.addModule; this.addModule = this.workletProto.addModule;
}; }
overrideWorker() { overrideWorker() {
this.ctx.override(this.window, 'Worker', (target, that, args) => { this.ctx.override(
this.window,
'Worker',
(target, that, args) => {
if (!args.length) return new target(...args); if (!args.length) return new target(...args);
let [ url, options = {} ] = args; let [url, options = {}] = args;
const event = new HookEvent({ url, options }, target, that); const event = new HookEvent({ url, options }, target, that);
this.emit('worker', event); this.emit('worker', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return new event.target(...[ event.data.url, event.data.options ]); return new event.target(
}, true); ...[event.data.url, event.data.options]
}; );
},
true
);
}
overrideAddModule() { overrideAddModule() {
this.ctx.override(this.workletProto, 'addModule', (target, that, args) => { this.ctx.override(
this.workletProto,
'addModule',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ url, options = {} ] = args; let [url, options = {}] = args;
const event = new HookEvent({ url, options }, target, that); const event = new HookEvent({ url, options }, target, that);
this.emit('addModule', event); this.emit('addModule', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.url, event.data.options); return event.target.call(
}); event.that,
}; event.data.url,
event.data.options
);
}
);
}
overridePostMessage() { overridePostMessage() {
this.ctx.override(this.workerProto, 'postMessage', (target, that, args) => { this.ctx.override(
this.workerProto,
'postMessage',
(target, that, args) => {
if (!args.length) return target.apply(that, args); if (!args.length) return target.apply(that, args);
let [ message, transfer = [] ] = args; let [message, transfer = []] = args;
const event = new HookEvent({ message, transfer }, target, that); const event = new HookEvent(
{ message, transfer },
target,
that
);
this.emit('postMessage', event); this.emit('postMessage', event);
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.message, event.data.transfer); return event.target.call(
}); event.that,
}; event.data.message,
event.data.transfer
);
}
);
}
overrideImportScripts() { overrideImportScripts() {
this.ctx.override(this.window, 'importScripts', (target, that, scripts) => { this.ctx.override(
this.window,
'importScripts',
(target, that, scripts) => {
if (!scripts.length) return target.apply(that, scripts); if (!scripts.length) return target.apply(that, scripts);
const event = new HookEvent({ scripts }, target, that); const event = new HookEvent({ scripts }, target, that);
@ -59,8 +89,9 @@ class Workers extends EventEmitter {
if (event.intercepted) return event.returnValue; if (event.intercepted) return event.returnValue;
return event.target.apply(event.that, event.data.scripts); return event.target.apply(event.that, event.data.scripts);
}); }
}; );
}; }
}
export default Workers; export default Workers;

View file

@ -3,15 +3,30 @@
// Do not use any browser or node-specific API! // Do not use any browser or node-specific API!
// ------------------------------------------------------------- // -------------------------------------------------------------
export const xor = { export const xor = {
encode(str){ encode(str) {
if (!str) return str; if (!str) return str;
return encodeURIComponent(str.toString().split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char).join('')); return encodeURIComponent(
str
.toString()
.split('')
.map((char, ind) =>
ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 2) : char
)
.join('')
);
}, },
decode(str){ decode(str) {
if (!str) return str; if (!str) return str;
let [ input, ...search ] = str.split('?'); let [input, ...search] = str.split('?');
return decodeURIComponent(input).split('').map((char, ind) => ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 2) : char).join('') + (search.length ? '?' + search.join('?') : ''); return (
decodeURIComponent(input)
.split('')
.map((char, ind) =>
ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 2) : char
)
.join('') + (search.length ? '?' + search.join('?') : '')
);
}, },
}; };
@ -27,10 +42,12 @@ export const plain = {
}; };
export const base64 = { export const base64 = {
encode(str){ encode(str) {
if (!str) return str; if (!str) return str;
str = str.toString(); str = str.toString();
const b64chs = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='); const b64chs = Array.from(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
);
let u32; let u32;
let c0; let c0;
let c1; let c1;
@ -38,21 +55,95 @@ export const base64 = {
let asc = ''; let asc = '';
let pad = str.length % 3; let pad = str.length % 3;
for (let i = 0; i < str.length;) { for (let i = 0; i < str.length; ) {
if((c0 = str.charCodeAt(i++)) > 255 || (c1 = str.charCodeAt(i++)) > 255 || (c2 = str.charCodeAt(i++)) > 255)throw new TypeError('invalid character found'); if (
(c0 = str.charCodeAt(i++)) > 255 ||
(c1 = str.charCodeAt(i++)) > 255 ||
(c2 = str.charCodeAt(i++)) > 255
)
throw new TypeError('invalid character found');
u32 = (c0 << 16) | (c1 << 8) | c2; u32 = (c0 << 16) | (c1 << 8) | c2;
asc += b64chs[u32 >> 18 & 63] asc +=
+ b64chs[u32 >> 12 & 63] b64chs[(u32 >> 18) & 63] +
+ b64chs[u32 >> 6 & 63] b64chs[(u32 >> 12) & 63] +
+ b64chs[u32 & 63]; b64chs[(u32 >> 6) & 63] +
b64chs[u32 & 63];
} }
return encodeURIComponent(pad ? asc.slice(0, pad - 3) + '==='.substr(pad) : asc); return encodeURIComponent(
pad ? asc.slice(0, pad - 3) + '==='.substr(pad) : asc
);
}, },
decode(str){ decode(str) {
if (!str) return str; if (!str) return str;
str = decodeURIComponent(str.toString()); str = decodeURIComponent(str.toString());
const b64tab = {"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"+":62,"/":63,"=":64}; const b64tab = {
0: 52,
1: 53,
2: 54,
3: 55,
4: 56,
5: 57,
6: 58,
7: 59,
8: 60,
9: 61,
A: 0,
B: 1,
C: 2,
D: 3,
E: 4,
F: 5,
G: 6,
H: 7,
I: 8,
J: 9,
K: 10,
L: 11,
M: 12,
N: 13,
O: 14,
P: 15,
Q: 16,
R: 17,
S: 18,
T: 19,
U: 20,
V: 21,
W: 22,
X: 23,
Y: 24,
Z: 25,
a: 26,
b: 27,
c: 28,
d: 29,
e: 30,
f: 31,
g: 32,
h: 33,
i: 34,
j: 35,
k: 36,
l: 37,
m: 38,
n: 39,
o: 40,
p: 41,
q: 42,
r: 43,
s: 44,
t: 45,
u: 46,
v: 47,
w: 48,
x: 49,
y: 50,
z: 51,
'+': 62,
'/': 63,
'=': 64,
};
str = str.replace(/\s+/g, ''); str = str.replace(/\s+/g, '');
str += '=='.slice(2 - (str.length & 3)); str += '=='.slice(2 - (str.length & 3));
let u24; let u24;
@ -60,15 +151,23 @@ export const base64 = {
let r1; let r1;
let r2; let r2;
for (let i = 0; i < str.length;) { for (let i = 0; i < str.length; ) {
u24 = b64tab[str.charAt(i++)] << 18 u24 =
| b64tab[str.charAt(i++)] << 12 (b64tab[str.charAt(i++)] << 18) |
| (r1 = b64tab[str.charAt(i++)]) << 6 (b64tab[str.charAt(i++)] << 12) |
| (r2 = b64tab[str.charAt(i++)]); ((r1 = b64tab[str.charAt(i++)]) << 6) |
bin += r1 === 64 ? String.fromCharCode(u24 >> 16 & 255) (r2 = b64tab[str.charAt(i++)]);
: r2 === 64 ? String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255) bin +=
: String.fromCharCode(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255); r1 === 64
}; ? String.fromCharCode((u24 >> 16) & 255)
: r2 === 64
? String.fromCharCode((u24 >> 16) & 255, (u24 >> 8) & 255)
: String.fromCharCode(
(u24 >> 16) & 255,
(u24 >> 8) & 255,
u24 & 255
);
}
return bin; return bin;
}, },
}; };

View file

@ -8,17 +8,16 @@ function validateCookie(cookie, meta, js = false) {
if (cookie.httpOnly && !!js) return false; if (cookie.httpOnly && !!js) return false;
if (cookie.domain.startsWith('.')) { if (cookie.domain.startsWith('.')) {
if (!meta.url.hostname.endsWith(cookie.domain.slice(1))) return false; if (!meta.url.hostname.endsWith(cookie.domain.slice(1))) return false;
return true; return true;
}; }
if (cookie.domain !== meta.url.hostname) return false; if (cookie.domain !== meta.url.hostname) return false;
if (cookie.secure && meta.url.protocol === 'http:') return false; if (cookie.secure && meta.url.protocol === 'http:') return false;
if (!meta.url.pathname.startsWith(cookie.path)) return false; if (!meta.url.pathname.startsWith(cookie.path)) return false;
return true; return true;
}; }
async function db(openDB) { async function db(openDB) {
const db = await openDB('__op', 1, { const db = await openDB('__op', 1, {
@ -31,8 +30,7 @@ async function db(openDB) {
}); });
db.transaction(['cookies'], 'readwrite').store.index('path'); db.transaction(['cookies'], 'readwrite').store.index('path');
return db; return db;
}; }
function serialize(cookies = [], meta, js) { function serialize(cookies = [], meta, js) {
let str = ''; let str = '';
@ -40,40 +38,39 @@ function serialize(cookies = [], meta, js) {
if (!validateCookie(cookie, meta, js)) continue; if (!validateCookie(cookie, meta, js)) continue;
if (str.length) str += '; '; if (str.length) str += '; ';
str += cookie.name; str += cookie.name;
str += '=' str += '=';
str += cookie.value; str += cookie.value;
}; }
return str; return str;
}; }
async function getCookies(db) { async function getCookies(db) {
const now = new Date(); const now = new Date();
return (await db.getAll('cookies')).filter(cookie => { return (await db.getAll('cookies')).filter((cookie) => {
let expired = false; let expired = false;
if (cookie.set) { if (cookie.set) {
if (cookie.maxAge) { if (cookie.maxAge) {
expired = (cookie.set.getTime() + (cookie.maxAge * 1e3)) < now; expired = cookie.set.getTime() + cookie.maxAge * 1e3 < now;
} else if (cookie.expires) { } else if (cookie.expires) {
expired = new Date(cookie.expires.toLocaleString()) < now; expired = new Date(cookie.expires.toLocaleString()) < now;
}; }
}; }
if (expired) { if (expired) {
db.delete('cookies', cookie.id); db.delete('cookies', cookie.id);
return false; return false;
}; }
return true; return true;
}); });
}; }
function setCookies(data, db, meta) { function setCookies(data, db, meta) {
if (!db) return false; if (!db) return false;
const cookies = setCookie(data, { const cookies = setCookie(data, {
decodeValues: false, decodeValues: false,
}) });
for (const cookie of cookies) { for (const cookie of cookies) {
if (!cookie.domain) cookie.domain = '.' + meta.url.hostname; if (!cookie.domain) cookie.domain = '.' + meta.url.hostname;
@ -81,15 +78,15 @@ function setCookies(data, db, meta) {
if (!cookie.domain.startsWith('.')) { if (!cookie.domain.startsWith('.')) {
cookie.domain = '.' + cookie.domain; cookie.domain = '.' + cookie.domain;
}; }
db.put('cookies', { db.put('cookies', {
...cookie, ...cookie,
id: `${cookie.domain}@${cookie.path}@${cookie.name}`, id: `${cookie.domain}@${cookie.path}@${cookie.name}`,
set: new Date(Date.now()), set: new Date(Date.now()),
}); });
}; }
return true; return true;
}; }
export { validateCookie, getCookies, setCookies, db , serialize }; export { validateCookie, getCookies, setCookies, db, serialize };

View file

@ -1,6 +1,6 @@
import { parse, walk, generate } from "css-tree"; import { parse, walk, generate } from 'css-tree';
import EventEmitter from "./events.js"; import EventEmitter from './events.js';
import parsel from "./parsel.js"; import parsel from './parsel.js';
class CSS extends EventEmitter { class CSS extends EventEmitter {
constructor(ctx) { constructor(ctx) {
@ -11,26 +11,29 @@ class CSS extends EventEmitter {
this.parse = parse; this.parse = parse;
this.walk = walk; this.walk = walk;
this.generate = generate; this.generate = generate;
}; }
rewrite(str, options) { rewrite(str, options) {
return this.recast(str, options, 'rewrite'); return this.recast(str, options, 'rewrite');
}; }
source(str, options) { source(str, options) {
return this.recast(str, options, 'source'); return this.recast(str, options, 'source');
}; }
recast(str, options, type) { recast(str, options, type) {
if (!str) return str; if (!str) return str;
str = new String(str).toString(); str = new String(str).toString();
try { try {
const ast = this.parse(str, { ...options, parseCustomProperty: true }); const ast = this.parse(str, {
this.walk(ast, node => { ...options,
parseCustomProperty: true,
});
this.walk(ast, (node) => {
this.emit(node.type, node, options, type); this.emit(node.type, node, options, type);
}); });
return this.generate(ast); return this.generate(ast);
} catch(e) { } catch (e) {
return str; return str;
}; }
}; }
}; }
export default CSS; export default CSS;

View file

@ -21,20 +21,22 @@
'use strict'; 'use strict';
var R = typeof Reflect === 'object' ? Reflect : null var R = typeof Reflect === 'object' ? Reflect : null;
var ReflectApply = R && typeof R.apply === 'function' var ReflectApply =
R && typeof R.apply === 'function'
? R.apply ? R.apply
: function ReflectApply(target, receiver, args) { : function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args); return Function.prototype.apply.call(target, receiver, args);
} };
var ReflectOwnKeys var ReflectOwnKeys;
if (R && typeof R.ownKeys === 'function') { if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys ReflectOwnKeys = R.ownKeys;
} else if (Object.getOwnPropertySymbols) { } else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) { ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target) return Object.getOwnPropertyNames(target).concat(
.concat(Object.getOwnPropertySymbols(target)); Object.getOwnPropertySymbols(target)
);
}; };
} else { } else {
ReflectOwnKeys = function ReflectOwnKeys(target) { ReflectOwnKeys = function ReflectOwnKeys(target) {
@ -46,9 +48,11 @@ function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning); if (console && console.warn) console.warn(warning);
} }
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { var NumberIsNaN =
Number.isNaN ||
function NumberIsNaN(value) {
return value !== value; return value !== value;
} };
function EventEmitter() { function EventEmitter() {
EventEmitter.init.call(this); EventEmitter.init.call(this);
@ -69,27 +73,35 @@ var defaultMaxListeners = 10;
function checkListener(listener) { function checkListener(listener) {
if (typeof listener !== 'function') { if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); throw new TypeError(
'The "listener" argument must be of type Function. Received type ' +
typeof listener
);
} }
} }
Object.defineProperty(EventEmitter, 'defaultMaxListeners', { Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true, enumerable: true,
get: function() { get: function () {
return defaultMaxListeners; return defaultMaxListeners;
}, },
set: function(arg) { set: function (arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); throw new RangeError(
'The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' +
arg +
'.'
);
} }
defaultMaxListeners = arg; defaultMaxListeners = arg;
} },
}); });
EventEmitter.init = function() { EventEmitter.init = function () {
if (
if (this._events === undefined || this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) { this._events === Object.getPrototypeOf(this)._events
) {
this._events = Object.create(null); this._events = Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} }
@ -101,7 +113,11 @@ EventEmitter.init = function() {
// that to be increased. Set to zero for unlimited. // that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); throw new RangeError(
'The value of "n" is out of range. It must be a non-negative number. Received ' +
n +
'.'
);
} }
this._maxListeners = n; this._maxListeners = n;
return this; return this;
@ -120,42 +136,39 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
EventEmitter.prototype.emit = function emit(type) { EventEmitter.prototype.emit = function emit(type) {
var args = []; var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error'); var doError = type === 'error';
var events = this._events; var events = this._events;
if (events !== undefined) if (events !== undefined) doError = doError && events.error === undefined;
doError = (doError && events.error === undefined); else if (!doError) return false;
else if (!doError)
return false;
// If there is no 'error' event listener then throw. // If there is no 'error' event listener then throw.
if (doError) { if (doError) {
var er; var er;
if (args.length > 0) if (args.length > 0) er = args[0];
er = args[0];
if (er instanceof Error) { if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show // Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception. // up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event throw er; // Unhandled 'error' event
} }
// At least give some kind of context to the user // At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); var err = new Error(
'Unhandled error.' + (er ? ' (' + er.message + ')' : '')
);
err.context = er; err.context = er;
throw err; // Unhandled 'error' event throw err; // Unhandled 'error' event
} }
var handler = events[type]; var handler = events[type];
if (handler === undefined) if (handler === undefined) return false;
return false;
if (typeof handler === 'function') { if (typeof handler === 'function') {
ReflectApply(handler, this, args); ReflectApply(handler, this, args);
} else { } else {
var len = handler.length; var len = handler.length;
var listeners = arrayClone(handler, len); var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i) for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
ReflectApply(listeners[i], this, args);
} }
return true; return true;
@ -176,8 +189,11 @@ function _addListener(target, type, listener, prepend) {
// To avoid recursion in the case that type === "newListener"! Before // To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener". // adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) { if (events.newListener !== undefined) {
target.emit('newListener', type, target.emit(
listener.listener ? listener.listener : listener); 'newListener',
type,
listener.listener ? listener.listener : listener
);
// Re-assign `events` because a newListener handler could have caused the // Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object // this._events to be assigned to a new object
@ -193,8 +209,9 @@ function _addListener(target, type, listener, prepend) {
} else { } else {
if (typeof existing === 'function') { if (typeof existing === 'function') {
// Adding the second element, need to change to array. // Adding the second element, need to change to array.
existing = events[type] = existing = events[type] = prepend
prepend ? [listener, existing] : [existing, listener]; ? [listener, existing]
: [existing, listener];
// If we've already got an array, just append. // If we've already got an array, just append.
} else if (prepend) { } else if (prepend) {
existing.unshift(listener); existing.unshift(listener);
@ -208,10 +225,15 @@ function _addListener(target, type, listener, prepend) {
existing.warned = true; existing.warned = true;
// No error code for this since it is a Warning // No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' + var w = new Error(
existing.length + ' ' + String(type) + ' listeners ' + 'Possible EventEmitter memory leak detected. ' +
existing.length +
' ' +
String(type) +
' listeners ' +
'added. Use emitter.setMaxListeners() to ' + 'added. Use emitter.setMaxListeners() to ' +
'increase limit'); 'increase limit'
);
w.name = 'MaxListenersExceededWarning'; w.name = 'MaxListenersExceededWarning';
w.emitter = target; w.emitter = target;
w.type = type; w.type = type;
@ -229,23 +251,30 @@ EventEmitter.prototype.addListener = function addListener(type, listener) {
EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener = EventEmitter.prototype.prependListener = function prependListener(
function prependListener(type, listener) { type,
listener
) {
return _addListener(this, type, listener, true); return _addListener(this, type, listener, true);
}; };
function onceWrapper() { function onceWrapper() {
if (!this.fired) { if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn); this.target.removeListener(this.type, this.wrapFn);
this.fired = true; this.fired = true;
if (arguments.length === 0) if (arguments.length === 0) return this.listener.call(this.target);
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments); return this.listener.apply(this.target, arguments);
} }
} }
function _onceWrap(target, type, listener) { function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; var state = {
fired: false,
wrapFn: undefined,
target: target,
type: type,
listener: listener,
};
var wrapped = onceWrapper.bind(state); var wrapped = onceWrapper.bind(state);
wrapped.listener = listener; wrapped.listener = listener;
state.wrapFn = wrapped; state.wrapFn = wrapped;
@ -258,31 +287,32 @@ EventEmitter.prototype.once = function once(type, listener) {
return this; return this;
}; };
EventEmitter.prototype.prependOnceListener = EventEmitter.prototype.prependOnceListener = function prependOnceListener(
function prependOnceListener(type, listener) { type,
listener
) {
checkListener(listener); checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener)); this.prependListener(type, _onceWrap(this, type, listener));
return this; return this;
}; };
// Emits a 'removeListener' event if and only if the listener was removed. // Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener = EventEmitter.prototype.removeListener = function removeListener(
function removeListener(type, listener) { type,
listener
) {
var list, events, position, i, originalListener; var list, events, position, i, originalListener;
checkListener(listener); checkListener(listener);
events = this._events; events = this._events;
if (events === undefined) if (events === undefined) return this;
return this;
list = events[type]; list = events[type];
if (list === undefined) if (list === undefined) return this;
return this;
if (list === listener || list.listener === listener) { if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) if (--this._eventsCount === 0) this._events = Object.create(null);
this._events = Object.create(null);
else { else {
delete events[type]; delete events[type];
if (events.removeListener) if (events.removeListener)
@ -299,34 +329,29 @@ EventEmitter.prototype.removeListener =
} }
} }
if (position < 0) if (position < 0) return this;
return this;
if (position === 0) if (position === 0) list.shift();
list.shift();
else { else {
spliceOne(list, position); spliceOne(list, position);
} }
if (list.length === 1) if (list.length === 1) events[type] = list[0];
events[type] = list[0];
if (events.removeListener !== undefined) if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener); this.emit('removeListener', type, originalListener || listener);
} }
return this; return this;
}; };
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
function removeAllListeners(type) {
var listeners, events, i; var listeners, events, i;
events = this._events; events = this._events;
if (events === undefined) if (events === undefined) return this;
return this;
// not listening for removeListener, no need to emit // not listening for removeListener, no need to emit
if (events.removeListener === undefined) { if (events.removeListener === undefined) {
@ -334,10 +359,8 @@ EventEmitter.prototype.removeAllListeners =
this._events = Object.create(null); this._events = Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} else if (events[type] !== undefined) { } else if (events[type] !== undefined) {
if (--this._eventsCount === 0) if (--this._eventsCount === 0) this._events = Object.create(null);
this._events = Object.create(null); else delete events[type];
else
delete events[type];
} }
return this; return this;
} }
@ -369,23 +392,22 @@ EventEmitter.prototype.removeAllListeners =
} }
return this; return this;
}; };
function _listeners(target, type, unwrap) { function _listeners(target, type, unwrap) {
var events = target._events; var events = target._events;
if (events === undefined) if (events === undefined) return [];
return [];
var evlistener = events[type]; var evlistener = events[type];
if (evlistener === undefined) if (evlistener === undefined) return [];
return [];
if (typeof evlistener === 'function') if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? return unwrap
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); ? unwrapListeners(evlistener)
: arrayClone(evlistener, evlistener.length);
} }
EventEmitter.prototype.listeners = function listeners(type) { EventEmitter.prototype.listeners = function listeners(type) {
@ -396,7 +418,7 @@ EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false); return _listeners(this, type, false);
}; };
EventEmitter.listenerCount = function(emitter, type) { EventEmitter.listenerCount = function (emitter, type) {
if (typeof emitter.listenerCount === 'function') { if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type); return emitter.listenerCount(type);
} else { } else {
@ -427,14 +449,12 @@ EventEmitter.prototype.eventNames = function eventNames() {
function arrayClone(arr, n) { function arrayClone(arr, n) {
var copy = new Array(n); var copy = new Array(n);
for (var i = 0; i < n; ++i) for (var i = 0; i < n; ++i) copy[i] = arr[i];
copy[i] = arr[i];
return copy; return copy;
} }
function spliceOne(list, index) { function spliceOne(list, index) {
for (; index + 1 < list.length; index++) for (; index + 1 < list.length; index++) list[index] = list[index + 1];
list[index] = list[index + 1];
list.pop(); list.pop();
} }
@ -458,11 +478,13 @@ function once(emitter, name) {
emitter.removeListener('error', errorListener); emitter.removeListener('error', errorListener);
} }
resolve([].slice.call(arguments)); resolve([].slice.call(arguments));
}; }
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== 'error') { if (name !== 'error') {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); addErrorHandlerIfEventEmitter(emitter, errorListener, {
once: true,
});
} }
}); });
} }
@ -492,6 +514,9 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
listener(arg); listener(arg);
}); });
} else { } else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); throw new TypeError(
'The "emitter" argument must be of type EventEmitter. Received type ' +
typeof emitter
);
} }
} }

View file

@ -7,32 +7,44 @@ class HTML extends EventEmitter {
this.ctx = ctx; this.ctx = ctx;
this.rewriteUrl = ctx.rewriteUrl; this.rewriteUrl = ctx.rewriteUrl;
this.sourceUrl = ctx.sourceUrl; this.sourceUrl = ctx.sourceUrl;
}; }
rewrite(str, options = {}) { rewrite(str, options = {}) {
if (!str) return str; if (!str) return str;
return this.recast(str, node => { return this.recast(
str,
(node) => {
if (node.tagName) this.emit('element', node, 'rewrite'); if (node.tagName) this.emit('element', node, 'rewrite');
if (node.attr) this.emit('attr', node, 'rewrite'); if (node.attr) this.emit('attr', node, 'rewrite');
if (node.nodeName === '#text') this.emit('text', node, 'rewrite'); if (node.nodeName === '#text')
}, options) this.emit('text', node, 'rewrite');
}; },
options
);
}
source(str, options = {}) { source(str, options = {}) {
if (!str) return str; if (!str) return str;
return this.recast(str, node => { return this.recast(
str,
(node) => {
if (node.tagName) this.emit('element', node, 'source'); if (node.tagName) this.emit('element', node, 'source');
if (node.attr) this.emit('attr', node, 'source'); if (node.attr) this.emit('attr', node, 'source');
if (node.nodeName === '#text') this.emit('text', node, 'source'); if (node.nodeName === '#text')
}, options) this.emit('text', node, 'source');
}; },
options
);
}
recast(str, fn, options = {}) { recast(str, fn, options = {}) {
try { try {
const ast = (options.document ? parse : parseFragment)(new String(str).toString()); const ast = (options.document ? parse : parseFragment)(
new String(str).toString()
);
this.iterate(ast, fn, options); this.iterate(ast, fn, options);
return serialize(ast); return serialize(ast);
} catch(e) { } catch (e) {
return str; return str;
}; }
}; }
iterate(ast, fn, fnOptions) { iterate(ast, fn, fnOptions) {
if (!ast) return ast; if (!ast) return ast;
@ -41,41 +53,55 @@ class HTML extends EventEmitter {
fn(element); fn(element);
if (ast.attrs) { if (ast.attrs) {
for (const attr of ast.attrs) { for (const attr of ast.attrs) {
if (!attr.skip) fn(new AttributeEvent(element, attr, fnOptions)); if (!attr.skip)
}; fn(new AttributeEvent(element, attr, fnOptions));
}; }
}; }
}
if (ast.childNodes) { if (ast.childNodes) {
for (const child of ast.childNodes) { for (const child of ast.childNodes) {
if (!child.skip) this.iterate(child, fn, fnOptions); if (!child.skip) this.iterate(child, fn, fnOptions);
}; }
}; }
if (ast.nodeName === '#text') { if (ast.nodeName === '#text') {
fn(new TextEvent(ast, new P5Element(ast.parentNode), false, fnOptions)); fn(
}; new TextEvent(
ast,
new P5Element(ast.parentNode),
false,
fnOptions
)
);
}
return ast; return ast;
}; }
wrapSrcset(str, meta = this.ctx.meta) { wrapSrcset(str, meta = this.ctx.meta) {
return str.split(',').map(src => { return str
.split(',')
.map((src) => {
const parts = src.trimStart().split(' '); const parts = src.trimStart().split(' ');
if (parts[0]) parts[0] = this.ctx.rewriteUrl(parts[0], meta); if (parts[0]) parts[0] = this.ctx.rewriteUrl(parts[0], meta);
return parts.join(' '); return parts.join(' ');
}).join(', '); })
}; .join(', ');
}
unwrapSrcset(str, meta = this.ctx.meta) { unwrapSrcset(str, meta = this.ctx.meta) {
return str.split(',').map(src => { return str
.split(',')
.map((src) => {
const parts = src.trimStart().split(' '); const parts = src.trimStart().split(' ');
if (parts[0]) parts[0] = this.ctx.sourceUrl(parts[0], meta); if (parts[0]) parts[0] = this.ctx.sourceUrl(parts[0], meta);
return parts.join(' '); return parts.join(' ');
}).join(', '); })
}; .join(', ');
}
static parse = parse; static parse = parse;
static parseFragment = parseFragment; static parseFragment = parseFragment;
static serialize = serialize; static serialize = serialize;
}; }
class P5Element extends EventEmitter { class P5Element extends EventEmitter {
constructor(node, stream = false, options = {}) { constructor(node, stream = false, options = {}) {
@ -83,96 +109,104 @@ class P5Element extends EventEmitter {
this.stream = stream; this.stream = stream;
this.node = node; this.node = node;
this.options = options; this.options = options;
}; }
setAttribute(name, value) { setAttribute(name, value) {
for (const attr of this.attrs) { for (const attr of this.attrs) {
if (attr.name === name) { if (attr.name === name) {
attr.value = value; attr.value = value;
return true; return true;
}; }
}; }
this.attrs.push( this.attrs.push({
{
name, name,
value, value,
});
} }
);
};
getAttribute(name) { getAttribute(name) {
const attr = this.attrs.find(attr => attr.name === name) || {}; const attr = this.attrs.find((attr) => attr.name === name) || {};
return attr.value; return attr.value;
}; }
hasAttribute(name) { hasAttribute(name) {
return !!this.attrs.find(attr => attr.name === name); return !!this.attrs.find((attr) => attr.name === name);
}; }
removeAttribute(name) { removeAttribute(name) {
const i = this.attrs.findIndex(attr => attr.name === name); const i = this.attrs.findIndex((attr) => attr.name === name);
if (typeof i !== 'undefined') this.attrs.splice(i, 1); if (typeof i !== 'undefined') this.attrs.splice(i, 1);
}; }
get tagName() { get tagName() {
return this.node.tagName; return this.node.tagName;
}; }
set tagName(val) { set tagName(val) {
this.node.tagName = val; this.node.tagName = val;
}; }
get childNodes() { get childNodes() {
return !this.stream ? this.node.childNodes : null; return !this.stream ? this.node.childNodes : null;
}; }
get innerHTML() { get innerHTML() {
return !this.stream ? serialize( return !this.stream
{ ? serialize({
nodeName: '#document-fragment', nodeName: '#document-fragment',
childNodes: this.childNodes, childNodes: this.childNodes,
})
: null;
} }
) : null;
};
set innerHTML(val) { set innerHTML(val) {
if (!this.stream) this.node.childNodes = parseFragment(val).childNodes; if (!this.stream) this.node.childNodes = parseFragment(val).childNodes;
};
get outerHTML() {
return !this.stream ? serialize(
{
nodeName: '#document-fragment',
childNodes: [ this ],
} }
) : null; get outerHTML() {
}; return !this.stream
? serialize({
nodeName: '#document-fragment',
childNodes: [this],
})
: null;
}
set outerHTML(val) { set outerHTML(val) {
if (!this.stream) this.parentNode.childNodes.splice(this.parentNode.childNodes.findIndex(node => node === this.node), 1, ...parseFragment(val).childNodes); if (!this.stream)
}; this.parentNode.childNodes.splice(
this.parentNode.childNodes.findIndex(
(node) => node === this.node
),
1,
...parseFragment(val).childNodes
);
}
get textContent() { get textContent() {
if (this.stream) return null; if (this.stream) return null;
let str = ''; let str = '';
iterate(this.node, node => { iterate(this.node, (node) => {
if (node.nodeName === '#text') str += node.value; if (node.nodeName === '#text') str += node.value;
}); });
return str; return str;
}; }
set textContent(val) { set textContent(val) {
if (!this.stream) this.node.childNodes = [ if (!this.stream)
this.node.childNodes = [
{ {
nodeName: '#text', nodeName: '#text',
value: val, value: val,
parentNode: this.node parentNode: this.node,
} },
]; ];
}; }
get nodeName() { get nodeName() {
return this.node.nodeName; return this.node.nodeName;
} }
get parentNode() { get parentNode() {
return this.node.parentNode ? new P5Element(this.node.parentNode) : null; return this.node.parentNode
}; ? new P5Element(this.node.parentNode)
: null;
}
get attrs() { get attrs() {
return this.node.attrs; return this.node.attrs;
} }
get namespaceURI() { get namespaceURI() {
return this.node.namespaceURI; return this.node.namespaceURI;
} }
}; }
class AttributeEvent { class AttributeEvent {
constructor(node, attr, options = {}) { constructor(node, attr, options = {}) {
@ -180,9 +214,9 @@ class AttributeEvent {
this.attrs = node.attrs; this.attrs = node.attrs;
this.node = node; this.node = node;
this.options = options; this.options = options;
}; }
delete() { delete() {
const i = this.attrs.findIndex(attr => attr === this.attr); const i = this.attrs.findIndex((attr) => attr === this.attr);
this.attrs.splice(i, 1); this.attrs.splice(i, 1);
@ -191,25 +225,25 @@ class AttributeEvent {
}); });
return true; return true;
}; }
get name() { get name() {
return this.attr.name; return this.attr.name;
}; }
set name(val) { set name(val) {
this.attr.name = val; this.attr.name = val;
}; }
get value() { get value() {
return this.attr.value; return this.attr.value;
}; }
set value(val) { set value(val) {
this.attr.value = val; this.attr.value = val;
}; }
get deleted() { get deleted() {
return false; return false;
}; }
}; }
class TextEvent { class TextEvent {
constructor(node, element, stream = false, options = {}) { constructor(node, element, stream = false, options = {}) {
@ -217,21 +251,20 @@ class TextEvent {
this.node = node; this.node = node;
this.element = element; this.element = element;
this.options = options; this.options = options;
}; }
get nodeName() { get nodeName() {
return this.node.nodeName; return this.node.nodeName;
} }
get parentNode() { get parentNode() {
return this.element; return this.element;
}; }
get value() { get value() {
return this.stream ? this.node.text : this.node.value; return this.stream ? this.node.text : this.node.value;
}; }
set value(val) { set value(val) {
if (this.stream) this.node.text = val; if (this.stream) this.node.text = val;
else this.node.value = val; else this.node.value = val;
}; }
}; }
export default HTML; export default HTML;

View file

@ -4,25 +4,48 @@ import JS from './js.js';
import setCookie from 'set-cookie-parser'; import setCookie from 'set-cookie-parser';
import { xor, base64, plain } from './codecs.js'; import { xor, base64, plain } from './codecs.js';
import mimeTypes from './mime.js'; import mimeTypes from './mime.js';
import { validateCookie, db, getCookies, setCookies, serialize } from './cookie.js'; import {
import { attributes, isUrl, isForbidden, isHtml, isSrcset, isStyle, text, injectHead, createInjection } from './rewrite.html.js'; validateCookie,
db,
getCookies,
setCookies,
serialize,
} from './cookie.js';
import {
attributes,
isUrl,
isForbidden,
isHtml,
isSrcset,
isStyle,
text,
injectHead,
createInjection,
} from './rewrite.html.js';
import { importStyle, url } from './rewrite.css.js'; import { importStyle, url } from './rewrite.css.js';
//import { call, destructureDeclaration, dynamicImport, getProperty, importDeclaration, setProperty, sourceMethods, wrapEval, wrapIdentifier } from './rewrite.script.js'; //import { call, destructureDeclaration, dynamicImport, getProperty, importDeclaration, setProperty, sourceMethods, wrapEval, wrapIdentifier } from './rewrite.script.js';
import { dynamicImport, identifier, importDeclaration, property, unwrap, wrapEval } from './rewrite.script.js'; import {
dynamicImport,
identifier,
importDeclaration,
property,
unwrap,
wrapEval,
} from './rewrite.script.js';
import { openDB } from 'idb'; import { openDB } from 'idb';
import parsel from './parsel.js'; import parsel from './parsel.js';
import UVClient from '../client/index.js'; import UVClient from '../client/index.js';
import Bowser from 'bowser'; import Bowser from 'bowser';
const valid_chars =
const valid_chars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"; "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~";
const reserved_chars = "%"; const reserved_chars = '%';
class Ultraviolet { class Ultraviolet {
constructor(options = {}) { constructor(options = {}) {
this.prefix = options.prefix || '/service/'; this.prefix = options.prefix || '/service/';
//this.urlRegex = /^(#|about:|data:|mailto:|javascript:)/; //this.urlRegex = /^(#|about:|data:|mailto:|javascript:)/;
this.urlRegex = /^(#|about:|data:|mailto:)/ this.urlRegex = /^(#|about:|data:|mailto:)/;
this.rewriteUrl = options.rewriteUrl || this.rewriteUrl; this.rewriteUrl = options.rewriteUrl || this.rewriteUrl;
this.sourceUrl = options.sourceUrl || this.sourceUrl; this.sourceUrl = options.sourceUrl || this.sourceUrl;
this.encodeUrl = options.encodeUrl || this.encodeUrl; this.encodeUrl = options.encodeUrl || this.encodeUrl;
@ -42,7 +65,10 @@ class Ultraviolet {
this.parsel = parsel; this.parsel = parsel;
this.openDB = this.constructor.openDB; this.openDB = this.constructor.openDB;
this.Bowser = this.constructor.Bowser; this.Bowser = this.constructor.Bowser;
this.client = typeof self !== 'undefined' ? new UVClient((options.window || self)) : null; this.client =
typeof self !== 'undefined'
? new UVClient(options.window || self)
: null;
this.master = '__uv'; this.master = '__uv';
this.dataPrefix = '__uv$'; this.dataPrefix = '__uv$';
this.attributePrefix = '__uv'; this.attributePrefix = '__uv';
@ -65,71 +91,82 @@ class Ultraviolet {
serialize, serialize,
setCookie, setCookie,
}; };
}; }
rewriteUrl(str, meta = this.meta) { rewriteUrl(str, meta = this.meta) {
str = new String(str).trim(); str = new String(str).trim();
if (!str || this.urlRegex.test(str)) return str; if (!str || this.urlRegex.test(str)) return str;
if (str.startsWith('javascript:')) { if (str.startsWith('javascript:')) {
return 'javascript:' + this.js.rewrite(str.slice('javascript:'.length)); return (
}; 'javascript:' + this.js.rewrite(str.slice('javascript:'.length))
);
}
try { try {
return meta.origin + this.prefix + this.encodeUrl(new URL(str, meta.base).href); return (
} catch(e) { meta.origin +
this.prefix +
this.encodeUrl(new URL(str, meta.base).href)
);
} catch (e) {
return meta.origin + this.prefix + this.encodeUrl(str); return meta.origin + this.prefix + this.encodeUrl(str);
}; }
}; }
sourceUrl(str, meta = this.meta) { sourceUrl(str, meta = this.meta) {
if (!str || this.urlRegex.test(str)) return str; if (!str || this.urlRegex.test(str)) return str;
try { try {
return new URL( return new URL(
this.decodeUrl(str.slice(this.prefix.length + meta.origin.length)), this.decodeUrl(
str.slice(this.prefix.length + meta.origin.length)
),
meta.base meta.base
).href; ).href;
} catch(e) { } catch (e) {
return this.decodeUrl(str.slice(this.prefix.length + meta.origin.length)); return this.decodeUrl(
}; str.slice(this.prefix.length + meta.origin.length)
}; );
}
}
encodeUrl(str) { encodeUrl(str) {
return encodeURIComponent(str); return encodeURIComponent(str);
}; }
decodeUrl(str) { decodeUrl(str) {
return decodeURIComponent(str); return decodeURIComponent(str);
}; }
encodeProtocol(protocol) { encodeProtocol(protocol) {
protocol = protocol.toString(); protocol = protocol.toString();
let result = ''; let result = '';
for(let i = 0; i < protocol.length; i++){ for (let i = 0; i < protocol.length; i++) {
const char = protocol[i]; const char = protocol[i];
if(valid_chars.includes(char) && !reserved_chars.includes(char)){ if (valid_chars.includes(char) && !reserved_chars.includes(char)) {
result += char; result += char;
}else{ } else {
const code = char.charCodeAt(); const code = char.charCodeAt();
result += '%' + code.toString(16).padStart(2, 0); result += '%' + code.toString(16).padStart(2, 0);
} }
} }
return result; return result;
}; }
decodeProtocol(protocol) { decodeProtocol(protocol) {
if(typeof protocol != 'string')throw new TypeError('protocol must be a string'); if (typeof protocol != 'string')
throw new TypeError('protocol must be a string');
let result = ''; let result = '';
for(let i = 0; i < protocol.length; i++){ for (let i = 0; i < protocol.length; i++) {
const char = protocol[i]; const char = protocol[i];
if(char == '%'){ if (char == '%') {
const code = parseInt(protocol.slice(i + 1, i + 3), 16); const code = parseInt(protocol.slice(i + 1, i + 3), 16);
const decoded = String.fromCharCode(code); const decoded = String.fromCharCode(code);
result += decoded; result += decoded;
i += 2; i += 2;
}else{ } else {
result += char; result += char;
} }
} }
@ -151,31 +188,31 @@ class Ultraviolet {
wrapEval(this); wrapEval(this);
identifier(this); identifier(this);
unwrap(this); unwrap(this);
}; }
get rewriteHtml() { get rewriteHtml() {
return this.html.rewrite.bind(this.html); return this.html.rewrite.bind(this.html);
}; }
get sourceHtml() { get sourceHtml() {
return this.html.source.bind(this.html); return this.html.source.bind(this.html);
}; }
get rewriteCSS() { get rewriteCSS() {
return this.css.rewrite.bind(this.css); return this.css.rewrite.bind(this.css);
}; }
get sourceCSS() { get sourceCSS() {
return this.css.source.bind(this.css); return this.css.source.bind(this.css);
}; }
get rewriteJS() { get rewriteJS() {
return this.js.rewrite.bind(this.js); return this.js.rewrite.bind(this.js);
}; }
get sourceJS() { get sourceJS() {
return this.js.source.bind(this.js); return this.js.source.bind(this.js);
}; }
static codec = { xor, base64, plain }; static codec = { xor, base64, plain };
static mime = mimeTypes; static mime = mimeTypes;
static setCookie = setCookie; static setCookie = setCookie;
static openDB = openDB; static openDB = openDB;
static Bowser = Bowser; static Bowser = Bowser;
}; }
export default Ultraviolet; export default Ultraviolet;
if (typeof self === 'object') self.Ultraviolet = Ultraviolet; if (typeof self === 'object') self.Ultraviolet = Ultraviolet;

View file

@ -27,13 +27,13 @@ class JS extends EventEmitter {
}; };
this.parse = parseScript /*parse*/; this.parse = parseScript /*parse*/;
this.generate = generate; this.generate = generate;
}; }
rewrite(str, data = {}) { rewrite(str, data = {}) {
return this.recast(str, data, 'rewrite'); return this.recast(str, data, 'rewrite');
}; }
source(str, data = {}) { source(str, data = {}) {
return this.recast(str, data, 'source'); return this.recast(str, data, 'source');
}; }
recast(str, data = {}, type = '') { recast(str, data = {}, type = '') {
try { try {
const output = []; const output = [];
@ -56,19 +56,26 @@ class JS extends EventEmitter {
this.emit(node.type, node, meta, type); this.emit(node.type, node, meta, type);
}); });
meta.changes.sort((a, b) => (a.start - b.start) || (a.end - b.end)); meta.changes.sort((a, b) => a.start - b.start || a.end - b.end);
for (const change of meta.changes) { for (const change of meta.changes) {
if ('start' in change && typeof change.start === 'number') output.push(str.slice(slice, change.start)); if ('start' in change && typeof change.start === 'number')
if (change.node) output.push(typeof change.node === 'string' ? change.node : generate(change.node, this.generationOptions)); output.push(str.slice(slice, change.start));
if ('end' in change && typeof change.end === 'number') slice = change.end; if (change.node)
}; output.push(
typeof change.node === 'string'
? change.node
: generate(change.node, this.generationOptions)
);
if ('end' in change && typeof change.end === 'number')
slice = change.end;
}
output.push(str.slice(slice)); output.push(str.slice(slice));
return output.join(''); return output.join('');
} catch(e) { } catch (e) {
return str; return str;
}; }
}; }
iterate(ast, handler) { iterate(ast, handler) {
if (typeof ast != 'object' || !handler) return; if (typeof ast != 'object' || !handler) return;
walk(ast, null, handler); walk(ast, null, handler);
@ -78,16 +85,16 @@ class JS extends EventEmitter {
for (const child in node) { for (const child in node) {
if (child === 'parent') continue; if (child === 'parent') continue;
if (Array.isArray(node[child])) { if (Array.isArray(node[child])) {
node[child].forEach(entry => { node[child].forEach((entry) => {
if (entry) walk(entry, node, handler) if (entry) walk(entry, node, handler);
}); });
} else { } else {
if (node[child]) walk(node[child], node, handler); if (node[child]) walk(node[child], node, handler);
}; }
}; }
if (typeof node.iterateEnd === 'function') node.iterateEnd(); if (typeof node.iterateEnd === 'function') node.iterateEnd();
}; }
}; }
}; }
export default JS; export default JS;

View file

@ -5,18 +5,18 @@
* MIT Licensed * MIT Licensed
*/ */
'use strict' 'use strict';
/** /**
* Module dependencies. * Module dependencies.
* @private * @private
*/ */
var $exports = {} var $exports = {};
import db from "mime-db"; import db from 'mime-db';
var extname = function(path = '') { var extname = function (path = '') {
if (!path.includes('.')) return ''; if (!path.includes('.')) return '';
const map = path.split('.'); const map = path.split('.');
@ -28,24 +28,24 @@ var extname = function(path = '') {
* @private * @private
*/ */
var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/ var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
var TEXT_TYPE_REGEXP = /^text\//i var TEXT_TYPE_REGEXP = /^text\//i;
/** /**
* Module exports. * Module exports.
* @public * @public
*/ */
$exports.charset = charset $exports.charset = charset;
$exports.charsets = { lookup: charset } $exports.charsets = { lookup: charset };
$exports.contentType = contentType $exports.contentType = contentType;
$exports.extension = extension $exports.extension = extension;
$exports.extensions = Object.create(null) $exports.extensions = Object.create(null);
$exports.lookup = lookup $exports.lookup = lookup;
$exports.types = Object.create(null) $exports.types = Object.create(null);
// Populate the extensions/types maps // Populate the extensions/types maps
populateMaps($exports.extensions, $exports.types) populateMaps($exports.extensions, $exports.types);
/** /**
* Get the default charset for a MIME type. * Get the default charset for a MIME type.
@ -54,25 +54,25 @@ populateMaps($exports.extensions, $exports.types)
* @return {boolean|string} * @return {boolean|string}
*/ */
function charset (type) { function charset(type) {
if (!type || typeof type !== 'string') { if (!type || typeof type !== 'string') {
return false return false;
} }
// TODO: use media-typer // TODO: use media-typer
var match = EXTRACT_TYPE_REGEXP.exec(type) var match = EXTRACT_TYPE_REGEXP.exec(type);
var mime = match && db[match[1].toLowerCase()] var mime = match && db[match[1].toLowerCase()];
if (mime && mime.charset) { if (mime && mime.charset) {
return mime.charset return mime.charset;
} }
// default text/* to utf-8 // default text/* to utf-8
if (match && TEXT_TYPE_REGEXP.test(match[1])) { if (match && TEXT_TYPE_REGEXP.test(match[1])) {
return 'UTF-8' return 'UTF-8';
} }
return false return false;
} }
/** /**
@ -82,27 +82,25 @@ function charset (type) {
* @return {boolean|string} * @return {boolean|string}
*/ */
function contentType (str) { function contentType(str) {
// TODO: should this even be in this module? // TODO: should this even be in this module?
if (!str || typeof str !== 'string') { if (!str || typeof str !== 'string') {
return false return false;
} }
var mime = str.indexOf('/') === -1 var mime = str.indexOf('/') === -1 ? $exports.lookup(str) : str;
? $exports.lookup(str)
: str
if (!mime) { if (!mime) {
return false return false;
} }
// TODO: use content-type or other module // TODO: use content-type or other module
if (mime.indexOf('charset') === -1) { if (mime.indexOf('charset') === -1) {
var charset = $exports.charset(mime) var charset = $exports.charset(mime);
if (charset) mime += '; charset=' + charset.toLowerCase() if (charset) mime += '; charset=' + charset.toLowerCase();
} }
return mime return mime;
} }
/** /**
@ -112,22 +110,22 @@ function contentType (str) {
* @return {boolean|string} * @return {boolean|string}
*/ */
function extension (type) { function extension(type) {
if (!type || typeof type !== 'string') { if (!type || typeof type !== 'string') {
return false return false;
} }
// TODO: use media-typer // TODO: use media-typer
var match = EXTRACT_TYPE_REGEXP.exec(type) var match = EXTRACT_TYPE_REGEXP.exec(type);
// get extensions // get extensions
var exts = match && $exports.extensions[match[1].toLowerCase()] var exts = match && $exports.extensions[match[1].toLowerCase()];
if (!exts || !exts.length) { if (!exts || !exts.length) {
return false return false;
} }
return exts[0] return exts[0];
} }
/** /**
@ -137,21 +135,21 @@ function extension (type) {
* @return {boolean|string} * @return {boolean|string}
*/ */
function lookup (path) { function lookup(path) {
if (!path || typeof path !== 'string') { if (!path || typeof path !== 'string') {
return false return false;
} }
// get the extension ("ext" or ".ext" or full path) // get the extension ("ext" or ".ext" or full path)
var extension = extname('x.' + path) var extension = extname('x.' + path)
.toLowerCase() .toLowerCase()
.substr(1) .substr(1);
if (!extension) { if (!extension) {
return false return false;
} }
return $exports.types[extension] || false return $exports.types[extension] || false;
} }
/** /**
@ -159,40 +157,44 @@ function lookup (path) {
* @private * @private
*/ */
function populateMaps (extensions, types) { function populateMaps(extensions, types) {
// source preference (least -> most) // source preference (least -> most)
var preference = ['nginx', 'apache', undefined, 'iana'] var preference = ['nginx', 'apache', undefined, 'iana'];
Object.keys(db).forEach(function forEachMimeType (type) { Object.keys(db).forEach(function forEachMimeType(type) {
var mime = db[type] var mime = db[type];
var exts = mime.extensions var exts = mime.extensions;
if (!exts || !exts.length) { if (!exts || !exts.length) {
return return;
} }
// mime -> extensions // mime -> extensions
extensions[type] = exts extensions[type] = exts;
// extension -> mime // extension -> mime
for (var i = 0; i < exts.length; i++) { for (var i = 0; i < exts.length; i++) {
var extension = exts[i] var extension = exts[i];
if (types[extension]) { if (types[extension]) {
var from = preference.indexOf(db[types[extension]].source) var from = preference.indexOf(db[types[extension]].source);
var to = preference.indexOf(mime.source) var to = preference.indexOf(mime.source);
if (types[extension] !== 'application/octet-stream' && if (
(from > to || (from === to && types[extension].substr(0, 12) === 'application/'))) { types[extension] !== 'application/octet-stream' &&
(from > to ||
(from === to &&
types[extension].substr(0, 12) === 'application/'))
) {
// skip the remapping // skip the remapping
continue continue;
} }
} }
// set the extension -> mime // set the extension -> mime
types[extension] = type types[extension] = type;
} }
}) });
} }
export default $exports; export default $exports;

View file

@ -2,46 +2,73 @@ export default (function (exports) {
'use strict'; 'use strict';
const TOKENS = { const TOKENS = {
attribute: /\[\s*(?:(?<namespace>\*|[-\w]*)\|)?(?<name>[-\w\u{0080}-\u{FFFF}]+)\s*(?:(?<operator>\W?=)\s*(?<value>.+?)\s*(?<caseSensitive>[iIsS])?\s*)?\]/gu, attribute:
/\[\s*(?:(?<namespace>\*|[-\w]*)\|)?(?<name>[-\w\u{0080}-\u{FFFF}]+)\s*(?:(?<operator>\W?=)\s*(?<value>.+?)\s*(?<caseSensitive>[iIsS])?\s*)?\]/gu,
id: /#(?<name>(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu, id: /#(?<name>(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,
class: /\.(?<name>(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu, class: /\.(?<name>(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,
comma: /\s*,\s*/g, // must be before combinator comma: /\s*,\s*/g, // must be before combinator
combinator: /\s*[\s>+~]\s*/g, // this must be after attribute combinator: /\s*[\s>+~]\s*/g, // this must be after attribute
"pseudo-element": /::(?<name>[-\w\u{0080}-\u{FFFF}]+)(?:\((?<argument>¶+)\))?/gu, // this must be before pseudo-class 'pseudo-element':
"pseudo-class": /:(?<name>[-\w\u{0080}-\u{FFFF}]+)(?:\((?<argument>¶+)\))?/gu, /::(?<name>[-\w\u{0080}-\u{FFFF}]+)(?:\((?<argument>¶+)\))?/gu, // this must be before pseudo-class
type: /(?:(?<namespace>\*|[-\w]*)\|)?(?<name>[-\w\u{0080}-\u{FFFF}]+)|\*/gu // this must be last 'pseudo-class':
/:(?<name>[-\w\u{0080}-\u{FFFF}]+)(?:\((?<argument>¶+)\))?/gu,
type: /(?:(?<namespace>\*|[-\w]*)\|)?(?<name>[-\w\u{0080}-\u{FFFF}]+)|\*/gu, // this must be last
}; };
const TOKENS_WITH_PARENS = new Set(["pseudo-class", "pseudo-element"]); const TOKENS_WITH_PARENS = new Set(['pseudo-class', 'pseudo-element']);
const TOKENS_WITH_STRINGS = new Set([...TOKENS_WITH_PARENS, "attribute"]); const TOKENS_WITH_STRINGS = new Set([...TOKENS_WITH_PARENS, 'attribute']);
const TRIM_TOKENS = new Set(["combinator", "comma"]); const TRIM_TOKENS = new Set(['combinator', 'comma']);
const RECURSIVE_PSEUDO_CLASSES = new Set(["not", "is", "where", "has", "matches", "-moz-any", "-webkit-any", "nth-child", "nth-last-child"]); const RECURSIVE_PSEUDO_CLASSES = new Set([
'not',
'is',
'where',
'has',
'matches',
'-moz-any',
'-webkit-any',
'nth-child',
'nth-last-child',
]);
const RECURSIVE_PSEUDO_CLASSES_ARGS = { const RECURSIVE_PSEUDO_CLASSES_ARGS = {
"nth-child": /(?<index>[\dn+-]+)\s+of\s+(?<subtree>.+)/ 'nth-child': /(?<index>[\dn+-]+)\s+of\s+(?<subtree>.+)/,
}; };
RECURSIVE_PSEUDO_CLASSES["nth-last-child"] = RECURSIVE_PSEUDO_CLASSES_ARGS["nth-child"]; RECURSIVE_PSEUDO_CLASSES['nth-last-child'] =
RECURSIVE_PSEUDO_CLASSES_ARGS['nth-child'];
const TOKENS_FOR_RESTORE = Object.assign({}, TOKENS); const TOKENS_FOR_RESTORE = Object.assign({}, TOKENS);
TOKENS_FOR_RESTORE["pseudo-element"] = RegExp(TOKENS["pseudo-element"].source.replace("(?<argument>¶+)", "(?<argument>.+?)"), "gu"); TOKENS_FOR_RESTORE['pseudo-element'] = RegExp(
TOKENS_FOR_RESTORE["pseudo-class"] = RegExp(TOKENS["pseudo-class"].source.replace("(?<argument>¶+)", "(?<argument>.+)"), "gu"); TOKENS['pseudo-element'].source.replace(
'(?<argument>¶+)',
'(?<argument>.+?)'
),
'gu'
);
TOKENS_FOR_RESTORE['pseudo-class'] = RegExp(
TOKENS['pseudo-class'].source.replace(
'(?<argument>¶+)',
'(?<argument>.+)'
),
'gu'
);
function gobbleParens(text, i) { function gobbleParens(text, i) {
let str = "", stack = []; let str = '',
stack = [];
for (; i < text.length; i++) { for (; i < text.length; i++) {
let char = text[i]; let char = text[i];
if (char === "(") { if (char === '(') {
stack.push(char); stack.push(char);
} } else if (char === ')') {
else if (char === ")") {
if (stack.length > 0) { if (stack.length > 0) {
stack.pop(); stack.pop();
} } else {
else { throw new Error(
throw new Error("Closing paren without opening paren at " + i); 'Closing paren without opening paren at ' + i
);
} }
} }
@ -52,10 +79,10 @@ export default (function (exports) {
} }
} }
throw new Error("Opening paren without closing paren"); throw new Error('Opening paren without closing paren');
} }
function tokenizeBy (text, grammar) { function tokenizeBy(text, grammar) {
if (!text) { if (!text) {
return []; return [];
} }
@ -65,10 +92,11 @@ export default (function (exports) {
for (var token in grammar) { for (var token in grammar) {
let pattern = grammar[token]; let pattern = grammar[token];
for (var i=0; i < strarr.length; i++) { // Dont cache length as it changes during the loop for (var i = 0; i < strarr.length; i++) {
// Dont cache length as it changes during the loop
var str = strarr[i]; var str = strarr[i];
if (typeof str === "string") { if (typeof str === 'string') {
pattern.lastIndex = 0; pattern.lastIndex = 0;
var match = pattern.exec(str); var match = pattern.exec(str);
@ -86,7 +114,7 @@ export default (function (exports) {
args.push({ args.push({
type: token, type: token,
content, content,
...match.groups ...match.groups,
}); });
let after = str.slice(from + content.length + 1); let after = str.slice(from + content.length + 1);
@ -96,21 +124,20 @@ export default (function (exports) {
strarr.splice(i, 1, ...args); strarr.splice(i, 1, ...args);
} }
} }
} }
} }
let offset = 0; let offset = 0;
for (let i=0; i<strarr.length; i++) { for (let i = 0; i < strarr.length; i++) {
let token = strarr[i]; let token = strarr[i];
let length = token.length || token.content.length; let length = token.length || token.content.length;
if (typeof token === "object") { if (typeof token === 'object') {
token.pos = [offset, offset + length]; token.pos = [offset, offset + length];
if (TRIM_TOKENS.has(token.type)) { if (TRIM_TOKENS.has(token.type)) {
token.content = token.content.trim() || " "; token.content = token.content.trim() || ' ';
} }
} }
@ -120,7 +147,7 @@ export default (function (exports) {
return strarr; return strarr;
} }
function tokenize (selector) { function tokenize(selector) {
if (!selector) { if (!selector) {
return null; return null;
} }
@ -130,17 +157,27 @@ export default (function (exports) {
// Replace strings with whitespace strings (to preserve offsets) // Replace strings with whitespace strings (to preserve offsets)
let strings = []; let strings = [];
// FIXME Does not account for escaped backslashes before a quote // FIXME Does not account for escaped backslashes before a quote
selector = selector.replace(/(['"])(\\\1|.)+?\1/g, (str, quote, content, start) => { selector = selector.replace(
strings.push({str, start}); /(['"])(\\\1|.)+?\1/g,
return quote + "§".repeat(content.length) + quote; (str, quote, content, start) => {
}); strings.push({ str, start });
return quote + '§'.repeat(content.length) + quote;
}
);
// Now that strings are out of the way, extract parens and replace them with parens with whitespace (to preserve offsets) // Now that strings are out of the way, extract parens and replace them with parens with whitespace (to preserve offsets)
let parens = [], offset = 0, start; let parens = [],
while ((start = selector.indexOf("(", offset)) > -1) { offset = 0,
start;
while ((start = selector.indexOf('(', offset)) > -1) {
let str = gobbleParens(selector, start); let str = gobbleParens(selector, start);
parens.push({str, start}); parens.push({ str, start });
selector = selector.substring(0, start) + "(" + "¶".repeat(str.length - 2) + ")" + selector.substring(start + str.length); selector =
selector.substring(0, start) +
'(' +
'¶'.repeat(str.length - 2) +
')' +
selector.substring(start + str.length);
offset = start + str.length; offset = start + str.length;
} }
@ -151,14 +188,21 @@ export default (function (exports) {
function restoreNested(strings, regex, types) { function restoreNested(strings, regex, types) {
for (let str of strings) { for (let str of strings) {
for (let token of tokens) { for (let token of tokens) {
if (types.has(token.type) && token.pos[0] < str.start && str.start < token.pos[1]) { if (
types.has(token.type) &&
token.pos[0] < str.start &&
str.start < token.pos[1]
) {
let content = token.content; let content = token.content;
token.content = token.content.replace(regex, str.str); token.content = token.content.replace(regex, str.str);
if (token.content !== content) { // actually changed? if (token.content !== content) {
// actually changed?
// Re-evaluate groups // Re-evaluate groups
TOKENS_FOR_RESTORE[token.type].lastIndex = 0; TOKENS_FOR_RESTORE[token.type].lastIndex = 0;
let match = TOKENS_FOR_RESTORE[token.type].exec(token.content); let match = TOKENS_FOR_RESTORE[token.type].exec(
token.content
);
let groups = match.groups; let groups = match.groups;
Object.assign(token, groups); Object.assign(token, groups);
} }
@ -174,46 +218,45 @@ export default (function (exports) {
} }
// Convert a flat list of tokens into a tree of complex & compound selectors // Convert a flat list of tokens into a tree of complex & compound selectors
function nestTokens(tokens, {list = true} = {}) { function nestTokens(tokens, { list = true } = {}) {
if (list && tokens.find(t => t.type === "comma")) { if (list && tokens.find((t) => t.type === 'comma')) {
let selectors = [], temp = []; let selectors = [],
temp = [];
for (let i=0; i<tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type === "comma") { if (tokens[i].type === 'comma') {
if (temp.length === 0) { if (temp.length === 0) {
throw new Error("Incorrect comma at " + i); throw new Error('Incorrect comma at ' + i);
} }
selectors.push(nestTokens(temp, {list: false})); selectors.push(nestTokens(temp, { list: false }));
temp.length = 0; temp.length = 0;
} } else {
else {
temp.push(tokens[i]); temp.push(tokens[i]);
} }
} }
if (temp.length === 0) { if (temp.length === 0) {
throw new Error("Trailing comma"); throw new Error('Trailing comma');
} } else {
else { selectors.push(nestTokens(temp, { list: false }));
selectors.push(nestTokens(temp, {list: false}));
} }
return { type: "list", list: selectors }; return { type: 'list', list: selectors };
} }
for (let i=tokens.length - 1; i>=0; i--) { for (let i = tokens.length - 1; i >= 0; i--) {
let token = tokens[i]; let token = tokens[i];
if (token.type === "combinator") { if (token.type === 'combinator') {
let left = tokens.slice(0, i); let left = tokens.slice(0, i);
let right = tokens.slice(i + 1); let right = tokens.slice(i + 1);
return { return {
type: "complex", type: 'complex',
combinator: token.content, combinator: token.content,
left: nestTokens(left), left: nestTokens(left),
right: nestTokens(right) right: nestTokens(right),
}; };
} }
} }
@ -223,9 +266,11 @@ export default (function (exports) {
} }
// If we're here, there are no combinators, so it's just a list // If we're here, there are no combinators, so it's just a list
return tokens.length === 1? tokens[0] : { return tokens.length === 1
type: "compound", ? tokens[0]
list: [...tokens] // clone to avoid pointers messing up the AST : {
type: 'compound',
list: [...tokens], // clone to avoid pointers messing up the AST
}; };
} }
@ -235,16 +280,14 @@ export default (function (exports) {
return; return;
} }
if (node.type === "complex") { if (node.type === 'complex') {
walk(node.left, callback, o, node); walk(node.left, callback, o, node);
walk(node.right, callback, o, node); walk(node.right, callback, o, node);
} } else if (node.type === 'compound') {
else if (node.type === "compound") {
for (let n of node.list) { for (let n of node.list) {
walk(n, callback, o, node); walk(n, callback, o, node);
} }
} } else if (node.subtree && o && o.subtree) {
else if (node.subtree && o && o.subtree) {
walk(node.subtree, callback, o, node); walk(node.subtree, callback, o, node);
} }
@ -257,21 +300,22 @@ export default (function (exports) {
* @param options.recursive {Boolean} Whether to parse the arguments of pseudo-classes like :is(), :has() etc. Defaults to true. * @param options.recursive {Boolean} Whether to parse the arguments of pseudo-classes like :is(), :has() etc. Defaults to true.
* @param options.list {Boolean} Whether this can be a selector list (A, B, C etc). Defaults to true. * @param options.list {Boolean} Whether this can be a selector list (A, B, C etc). Defaults to true.
*/ */
function parse(selector, {recursive = true, list = true} = {}) { function parse(selector, { recursive = true, list = true } = {}) {
let tokens = tokenize(selector); let tokens = tokenize(selector);
if (!tokens) { if (!tokens) {
return null; return null;
} }
let ast = nestTokens(tokens, {list}); let ast = nestTokens(tokens, { list });
if (recursive) { if (recursive) {
walk(ast, node => { walk(ast, (node) => {
if (node.type === "pseudo-class" && node.argument) { if (node.type === 'pseudo-class' && node.argument) {
if (RECURSIVE_PSEUDO_CLASSES.has(node.name)) { if (RECURSIVE_PSEUDO_CLASSES.has(node.name)) {
let argument = node.argument; let argument = node.argument;
const childArg = RECURSIVE_PSEUDO_CLASSES_ARGS[node.name]; const childArg =
RECURSIVE_PSEUDO_CLASSES_ARGS[node.name];
if (childArg) { if (childArg) {
const match = childArg.exec(argument); const match = childArg.exec(argument);
if (!match) { if (!match) {
@ -282,7 +326,10 @@ export default (function (exports) {
argument = match.groups.subtree; argument = match.groups.subtree;
} }
if (argument) { if (argument) {
node.subtree = parse(argument, {recursive: true, list: true}); node.subtree = parse(argument, {
recursive: true,
list: true,
});
} }
} }
} }
@ -295,65 +342,72 @@ export default (function (exports) {
function specificityToNumber(specificity, base) { function specificityToNumber(specificity, base) {
base = base || Math.max(...specificity) + 1; base = base || Math.max(...specificity) + 1;
return specificity[0] * base ** 2 + specificity[1] * base + specificity[2]; return (
specificity[0] * base ** 2 + specificity[1] * base + specificity[2]
);
} }
function maxIndexOf(arr) { function maxIndexOf(arr) {
let max = arr[0], ret = 0; let max = arr[0],
ret = 0;
for (let i=0; i<arr.length; i++) { for (let i = 0; i < arr.length; i++) {
if (arr[i] > max) { if (arr[i] > max) {
ret = i; ret = i;
max = arr[i]; max = arr[i];
} }
} }
return arr.length === 0? -1 : ret; return arr.length === 0 ? -1 : ret;
} }
/** /**
* Calculate specificity of a selector. * Calculate specificity of a selector.
* If the selector is a list, the max specificity is returned. * If the selector is a list, the max specificity is returned.
*/ */
function specificity(selector, {format = "array"} = {}) { function specificity(selector, { format = 'array' } = {}) {
let ast = typeof selector === "object"? selector : parse(selector, {recursive: true}); let ast =
typeof selector === 'object'
? selector
: parse(selector, { recursive: true });
if (!ast) { if (!ast) {
return null; return null;
} }
if (ast.type === "list") { if (ast.type === 'list') {
// Return max specificity // Return max specificity
let base = 10; let base = 10;
let specificities = ast.list.map(s => { let specificities = ast.list.map((s) => {
let sp = specificity(s); let sp = specificity(s);
base = Math.max(base, ...sp); base = Math.max(base, ...sp);
return sp; return sp;
}); });
let numbers = specificities.map(s => specificityToNumber(s, base)); let numbers = specificities.map((s) =>
specificityToNumber(s, base)
);
let i = maxIndexOf(numbers); let i = maxIndexOf(numbers);
return specificities[i]; return specificities[i];
} }
let ret = [0, 0, 0]; let ret = [0, 0, 0];
walk(ast, node => { walk(ast, (node) => {
if (node.type === "id") { if (node.type === 'id') {
ret[0]++; ret[0]++;
} } else if (node.type === 'class' || node.type === 'attribute') {
else if (node.type === "class" || node.type === "attribute") {
ret[1]++; ret[1]++;
} } else if (
else if ((node.type === "type" && node.content !== "*") || node.type === "pseudo-element") { (node.type === 'type' && node.content !== '*') ||
node.type === 'pseudo-element'
) {
ret[2]++; ret[2]++;
} } else if (node.type === 'pseudo-class' && node.name !== 'where') {
else if (node.type === "pseudo-class" && node.name !== "where") {
if (RECURSIVE_PSEUDO_CLASSES.has(node.name) && node.subtree) { if (RECURSIVE_PSEUDO_CLASSES.has(node.name) && node.subtree) {
// Max of argument list // Max of argument list
let sub = specificity(node.subtree); let sub = specificity(node.subtree);
sub.forEach((s, i) => ret[i] += s); sub.forEach((s, i) => (ret[i] += s));
} } else {
else {
ret[1]++; ret[1]++;
} }
} }
@ -378,5 +432,4 @@ export default (function (exports) {
Object.defineProperty(exports, '__esModule', { value: true }); Object.defineProperty(exports, '__esModule', { value: true });
return exports; return exports;
})({});
}({}));

View file

@ -1,9 +1,12 @@
function url(ctx) { function url(ctx) {
const { css } = ctx; const { css } = ctx;
css.on('Url', (node, data, type) => { css.on('Url', (node, data, type) => {
node.value = type === 'rewrite' ? ctx.rewriteUrl(node.value) : ctx.sourceUrl(node.value); node.value =
type === 'rewrite'
? ctx.rewriteUrl(node.value)
: ctx.sourceUrl(node.value);
}); });
}; }
function importStyle(ctx) { function importStyle(ctx) {
const { css } = ctx; const { css } = ctx;
@ -12,9 +15,11 @@ function importStyle(ctx) {
const { data: url } = node.prelude.children.head; const { data: url } = node.prelude.children.head;
// Already handling Url's // Already handling Url's
if (url.type === 'Url') return false; if (url.type === 'Url') return false;
url.value = type === 'rewrite' ? ctx.rewriteUrl(url.value) : ctx.sourceUrl(url.value); url.value =
type === 'rewrite'
? ctx.rewriteUrl(url.value)
: ctx.sourceUrl(url.value);
}); });
}; }
export { url, importStyle }; export { url, importStyle };

View file

@ -3,20 +3,23 @@ function attributes(ctx, meta = ctx.meta) {
const origPrefix = attributePrefix + '-attr-'; const origPrefix = attributePrefix + '-attr-';
html.on('attr', (attr, type) => { html.on('attr', (attr, type) => {
if (attr.node.tagName === 'base' && attr.name === 'href' && attr.options.document) { if (
attr.node.tagName === 'base' &&
attr.name === 'href' &&
attr.options.document
) {
meta.base = new URL(attr.value, meta.url); meta.base = new URL(attr.value, meta.url);
}; }
if (type === 'rewrite' && isUrl(attr.name, attr.tagName)) { if (type === 'rewrite' && isUrl(attr.name, attr.tagName)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value); attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = ctx.rewriteUrl(attr.value, meta); attr.value = ctx.rewriteUrl(attr.value, meta);
}; }
if (type === 'rewrite' && isSrcset(attr.name)) { if (type === 'rewrite' && isSrcset(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value); attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = html.wrapSrcset(attr.value, meta); attr.value = html.wrapSrcset(attr.value, meta);
}; }
if (type === 'rewrite' && isHtml(attr.name)) { if (type === 'rewrite' && isHtml(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value); attr.node.setAttribute(origPrefix + attr.name, attr.value);
@ -25,28 +28,29 @@ function attributes(ctx, meta = ctx.meta) {
document: true, document: true,
injectHead: attr.options.injectHead || [], injectHead: attr.options.injectHead || [],
}); });
}; }
if (type === 'rewrite' && isStyle(attr.name)) { if (type === 'rewrite' && isStyle(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value); attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = ctx.rewriteCSS(attr.value, { context: 'declarationList', }); attr.value = ctx.rewriteCSS(attr.value, {
}; context: 'declarationList',
});
}
if (type === 'rewrite' && isForbidden(attr.name)) { if (type === 'rewrite' && isForbidden(attr.name)) {
attr.name = origPrefix + attr.name; attr.name = origPrefix + attr.name;
}; }
if (type === 'rewrite' && isEvent(attr.name)) { if (type === 'rewrite' && isEvent(attr.name)) {
attr.node.setAttribute(origPrefix + attr.name, attr.value); attr.node.setAttribute(origPrefix + attr.name, attr.value);
attr.value = js.rewrite(attr.value, meta); attr.value = js.rewrite(attr.value, meta);
}; }
if (type === 'source' && attr.name.startsWith(origPrefix)) { if (type === 'source' && attr.name.startsWith(origPrefix)) {
if (attr.node.hasAttribute(attr.name.slice(origPrefix.length))) attr.node.removeAttribute(attr.name.slice(origPrefix.length)); if (attr.node.hasAttribute(attr.name.slice(origPrefix.length)))
attr.node.removeAttribute(attr.name.slice(origPrefix.length));
attr.name = attr.name.slice(origPrefix.length); attr.name = attr.name.slice(origPrefix.length);
}; }
/* /*
if (isHtml(attr.name)) { if (isHtml(attr.name)) {
@ -62,30 +66,47 @@ function attributes(ctx, meta = ctx.meta) {
}; };
*/ */
}); });
}
};
function text(ctx, meta = ctx.meta) { function text(ctx, meta = ctx.meta) {
const { html, js, css, attributePrefix } = ctx; const { html, js, css, attributePrefix } = ctx;
html.on('text', (text, type) => { html.on('text', (text, type) => {
if (text.element.tagName === 'script') { if (text.element.tagName === 'script') {
text.value = type === 'rewrite' ? js.rewrite(text.value) : js.source(text.value); text.value =
}; type === 'rewrite'
? js.rewrite(text.value)
: js.source(text.value);
}
if (text.element.tagName === 'style') { if (text.element.tagName === 'style') {
text.value = type === 'rewrite' ? css.rewrite(text.value) : css.source(text.value); text.value =
}; type === 'rewrite'
? css.rewrite(text.value)
: css.source(text.value);
}
}); });
return true; return true;
}; }
function isUrl(name, tag) { function isUrl(name, tag) {
return tag === 'object' && name === 'data' || ['src', 'href', 'ping', 'movie', 'action', 'poster', 'profile', 'background'].indexOf(name) > -1; return (
}; (tag === 'object' && name === 'data') ||
[
'src',
'href',
'ping',
'movie',
'action',
'poster',
'profile',
'background',
].indexOf(name) > -1
);
}
function isEvent(name) { function isEvent(name) {
return [ return (
[
'onafterprint', 'onafterprint',
'onbeforeprint', 'onbeforeprint',
'onbeforeunload', 'onbeforeunload',
@ -155,8 +176,9 @@ function isEvent(name) {
'ontimeupdate', 'ontimeupdate',
'onvolumechange', 'onvolumechange',
'onwaiting', 'onwaiting',
].indexOf(name) > -1; ].indexOf(name) > -1
}; );
}
function injectHead(ctx) { function injectHead(ctx) {
const { html, js, css, attributePrefix } = ctx; const { html, js, css, attributePrefix } = ctx;
@ -166,13 +188,17 @@ function injectHead(ctx) {
if (element.tagName !== 'head') return false; if (element.tagName !== 'head') return false;
if (!('injectHead' in element.options)) return false; if (!('injectHead' in element.options)) return false;
element.childNodes.unshift( element.childNodes.unshift(...element.options.injectHead);
...element.options.injectHead
);
}); });
}; }
function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', config = '/uv.config.js', cookies = '', referrer = '') { function createInjection(
handler = '/uv.handler.js',
bundle = '/uv.bundle.js',
config = '/uv.config.js',
cookies = '',
referrer = ''
) {
return [ return [
{ {
tagName: 'script', tagName: 'script',
@ -180,7 +206,9 @@ function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', c
childNodes: [ childNodes: [
{ {
nodeName: '#text', nodeName: '#text',
value: `window.__uv$cookies = atob("${btoa(cookies)}");\nwindow.__uv$referrer = atob("${btoa(referrer)}");` value: `window.__uv$cookies = atob("${btoa(
cookies
)}");\nwindow.__uv$referrer = atob("${btoa(referrer)}");`,
}, },
], ],
attrs: [ attrs: [
@ -188,7 +216,7 @@ function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', c
name: '__uv-script', name: '__uv-script',
value: '1', value: '1',
skip: true, skip: true,
} },
], ],
skip: true, skip: true,
}, },
@ -202,7 +230,7 @@ function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', c
name: '__uv-script', name: '__uv-script',
value: '1', value: '1',
skip: true, skip: true,
} },
], ],
}, },
{ {
@ -215,7 +243,7 @@ function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', c
name: '__uv-script', name: '__uv-script',
value: '1', value: '1',
skip: true, skip: true,
} },
], ],
}, },
{ {
@ -228,27 +256,41 @@ function createInjection(handler = '/uv.handler.js', bundle = '/uv.bundle.js', c
name: '__uv-script', name: '__uv-script',
value: '1', value: '1',
skip: true, skip: true,
} },
], ],
} },
]; ];
}; }
function isForbidden(name) { function isForbidden(name) {
return ['http-equiv', 'integrity', 'sandbox', 'nonce', 'crossorigin'].indexOf(name) > -1; return (
}; ['http-equiv', 'integrity', 'sandbox', 'nonce', 'crossorigin'].indexOf(
name
) > -1
);
}
function isHtml(name){ function isHtml(name) {
return name === 'srcdoc'; return name === 'srcdoc';
}; }
function isStyle(name) { function isStyle(name) {
return name === 'style'; return name === 'style';
}; }
function isSrcset(name) { function isSrcset(name) {
return name === 'srcset' || name === 'imagesrcset'; return name === 'srcset' || name === 'imagesrcset';
}
export {
attributes,
createInjection,
text,
isUrl,
isEvent,
isForbidden,
isHtml,
isStyle,
isSrcset,
injectHead,
}; };
export { attributes, createInjection, text, isUrl, isEvent, isForbidden, isHtml, isStyle, isSrcset, injectHead };

View file

@ -10,92 +10,167 @@ function property(ctx) {
node: '__uv.$wrap((', node: '__uv.$wrap((',
start: node.property.start, start: node.property.start,
end: node.property.start, end: node.property.start,
}) });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
node: '))', node: '))',
start: node.property.end, start: node.property.end,
end: node.property.end, end: node.property.end,
}); });
}; };
}
}; if (
(!node.computed &&
if (!node.computed && node.property.name === 'location' && type === 'rewrite' || node.property.name === '__uv$location' && type === 'source') { node.property.name === 'location' &&
type === 'rewrite') ||
(node.property.name === '__uv$location' && type === 'source')
) {
data.changes.push({ data.changes.push({
start: node.property.start, start: node.property.start,
end: node.property.end, end: node.property.end,
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$location' : 'location' node:
type === 'rewrite'
? '__uv$setSource(__uv).__uv$location'
: 'location',
}); });
}; }
if (
if (!node.computed && node.property.name === 'top' && type === 'rewrite' || node.property.name === '__uv$top' && type === 'source') { (!node.computed &&
node.property.name === 'top' &&
type === 'rewrite') ||
(node.property.name === '__uv$top' && type === 'source')
) {
data.changes.push({ data.changes.push({
start: node.property.start, start: node.property.start,
end: node.property.end, end: node.property.end,
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$top' : 'top' node:
type === 'rewrite'
? '__uv$setSource(__uv).__uv$top'
: 'top',
}); });
}; }
if (!node.computed && node.property.name === 'parent' && type === 'rewrite' || node.property.name === '__uv$parent' && type === 'source') { if (
(!node.computed &&
node.property.name === 'parent' &&
type === 'rewrite') ||
(node.property.name === '__uv$parent' && type === 'source')
) {
data.changes.push({ data.changes.push({
start: node.property.start, start: node.property.start,
end: node.property.end, end: node.property.end,
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$parent' : 'parent' node:
type === 'rewrite'
? '__uv$setSource(__uv).__uv$parent'
: 'parent',
}); });
}; }
if (
if (!node.computed && node.property.name === 'postMessage' && type === 'rewrite') { !node.computed &&
node.property.name === 'postMessage' &&
type === 'rewrite'
) {
data.changes.push({ data.changes.push({
start: node.property.start, start: node.property.start,
end: node.property.end, end: node.property.end,
node:'__uv$setSource(__uv).postMessage', node: '__uv$setSource(__uv).postMessage',
}); });
}; }
if (
if (!node.computed && node.property.name === 'eval' && type === 'rewrite' || node.property.name === '__uv$eval' && type === 'source') { (!node.computed &&
node.property.name === 'eval' &&
type === 'rewrite') ||
(node.property.name === '__uv$eval' && type === 'source')
) {
data.changes.push({ data.changes.push({
start: node.property.start, start: node.property.start,
end: node.property.end, end: node.property.end,
node: type === 'rewrite' ? '__uv$setSource(__uv).__uv$eval' : 'eval' node:
type === 'rewrite'
? '__uv$setSource(__uv).__uv$eval'
: 'eval',
}); });
}; }
if (!node.computed && node.property.name === '__uv$setSource' && type === 'source' && node.parent.type === Syntax.CallExpression) { if (
!node.computed &&
node.property.name === '__uv$setSource' &&
type === 'source' &&
node.parent.type === Syntax.CallExpression
) {
const { parent, property } = node; const { parent, property } = node;
data.changes.push({ data.changes.push({
start: property.start - 1, start: property.start - 1,
end: parent.end, end: parent.end,
}); });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
start: property.start, start: property.start,
end: parent.end, end: parent.end,
}); });
}; };
}; }
}); });
}; }
function identifier(ctx) { function identifier(ctx) {
const { js } = ctx; const { js } = ctx;
js.on('Identifier', (node, data, type) => { js.on('Identifier', (node, data, type) => {
if (type !== 'rewrite') return false; if (type !== 'rewrite') return false;
const { parent } = node; const { parent } = node;
if (!['location', 'eval', 'parent', 'top'].includes(node.name)) return false; if (!['location', 'eval', 'parent', 'top'].includes(node.name))
if (parent.type === Syntax.VariableDeclarator && parent.id === node) return false; return false;
if ((parent.type === Syntax.AssignmentExpression || parent.type === Syntax.AssignmentPattern) && parent.left === node) return false; if (parent.type === Syntax.VariableDeclarator && parent.id === node)
if ((parent.type === Syntax.FunctionExpression || parent.type === Syntax.FunctionDeclaration) && parent.id === node) return false; return false;
if (parent.type === Syntax.MemberExpression && parent.property === node && !parent.computed) return false; if (
if (node.name === 'eval' && parent.type === Syntax.CallExpression && parent.callee === node) return false; (parent.type === Syntax.AssignmentExpression ||
if (parent.type === Syntax.Property && parent.key === node) return false; parent.type === Syntax.AssignmentPattern) &&
if (parent.type === Syntax.Property && parent.value === node && parent.shorthand) return false; parent.left === node
if (parent.type === Syntax.UpdateExpression && (parent.operator === '++' || parent.operator === '--')) return false; )
if ((parent.type === Syntax.FunctionExpression || parent.type === Syntax.FunctionDeclaration || parent.type === Syntax.ArrowFunctionExpression) && parent.params.indexOf(node) !== -1) return false; return false;
if (
(parent.type === Syntax.FunctionExpression ||
parent.type === Syntax.FunctionDeclaration) &&
parent.id === node
)
return false;
if (
parent.type === Syntax.MemberExpression &&
parent.property === node &&
!parent.computed
)
return false;
if (
node.name === 'eval' &&
parent.type === Syntax.CallExpression &&
parent.callee === node
)
return false;
if (parent.type === Syntax.Property && parent.key === node)
return false;
if (
parent.type === Syntax.Property &&
parent.value === node &&
parent.shorthand
)
return false;
if (
parent.type === Syntax.UpdateExpression &&
(parent.operator === '++' || parent.operator === '--')
)
return false;
if (
(parent.type === Syntax.FunctionExpression ||
parent.type === Syntax.FunctionDeclaration ||
parent.type === Syntax.ArrowFunctionExpression) &&
parent.params.indexOf(node) !== -1
)
return false;
if (parent.type === Syntax.MethodDefinition) return false; if (parent.type === Syntax.MethodDefinition) return false;
if (parent.type === Syntax.ClassDeclaration) return false; if (parent.type === Syntax.ClassDeclaration) return false;
if (parent.type === Syntax.RestElement) return false; if (parent.type === Syntax.RestElement) return false;
@ -105,10 +180,10 @@ function identifier(ctx) {
data.changes.push({ data.changes.push({
start: node.start, start: node.start,
end: node.end, end: node.end,
node: '__uv.$get(' + node.name + ')' node: '__uv.$get(' + node.name + ')',
}); });
}); });
}; }
function wrapEval(ctx) { function wrapEval(ctx) {
const { js } = ctx; const { js } = ctx;
@ -118,14 +193,14 @@ function wrapEval(ctx) {
if (node.callee.type !== 'Identifier') return false; if (node.callee.type !== 'Identifier') return false;
if (node.callee.name !== 'eval') return false; if (node.callee.name !== 'eval') return false;
const [ script ] = node.arguments; const [script] = node.arguments;
data.changes.push({ data.changes.push({
node: '__uv.js.rewrite(', node: '__uv.js.rewrite(',
start: script.start, start: script.start,
end: script.start, end: script.start,
}) });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
node: ')', node: ')',
start: script.end, start: script.end,
@ -133,21 +208,31 @@ function wrapEval(ctx) {
}); });
}; };
}); });
}; }
function importDeclaration(ctx) { function importDeclaration(ctx) {
const { js } = ctx; const { js } = ctx;
js.on(Syntax.Literal, (node, data, type) => { js.on(Syntax.Literal, (node, data, type) => {
if (!((node.parent.type === Syntax.ImportDeclaration || node.parent.type === Syntax.ExportAllDeclaration || node.parent.type === Syntax.ExportNamedDeclaration) if (
&& node.parent.source === node)) return false; !(
(node.parent.type === Syntax.ImportDeclaration ||
node.parent.type === Syntax.ExportAllDeclaration ||
node.parent.type === Syntax.ExportNamedDeclaration) &&
node.parent.source === node
)
)
return false;
data.changes.push({ data.changes.push({
start: node.start + 1, start: node.start + 1,
end: node.end - 1, end: node.end - 1,
node: type === 'rewrite' ? ctx.rewriteUrl(node.value) : ctx.sourceUrl(node.value) node:
type === 'rewrite'
? ctx.rewriteUrl(node.value)
: ctx.sourceUrl(node.value),
}); });
}); });
}; }
function dynamicImport(ctx) { function dynamicImport(ctx) {
const { js } = ctx; const { js } = ctx;
@ -157,8 +242,8 @@ function dynamicImport(ctx) {
node: '__uv.rewriteUrl(', node: '__uv.rewriteUrl(',
start: node.source.start, start: node.source.start,
end: node.source.start, end: node.source.start,
}) });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
node: ')', node: ')',
start: node.source.end, start: node.source.end,
@ -166,7 +251,7 @@ function dynamicImport(ctx) {
}); });
}; };
}); });
}; }
function unwrap(ctx) { function unwrap(ctx) {
const { js } = ctx; const { js } = ctx;
@ -174,17 +259,22 @@ function unwrap(ctx) {
if (type !== 'source') return false; if (type !== 'source') return false;
if (!isWrapped(node.callee)) return false; if (!isWrapped(node.callee)) return false;
switch(node.callee.property.name) { switch (node.callee.property.name) {
case '$wrap': case '$wrap':
if (!node.arguments || node.parent.type !== Syntax.MemberExpression || node.parent.property !== node) return false; if (
const [ property ] = node.arguments; !node.arguments ||
node.parent.type !== Syntax.MemberExpression ||
node.parent.property !== node
)
return false;
const [property] = node.arguments;
data.changes.push({ data.changes.push({
start: node.callee.start, start: node.callee.start,
end: property.start, end: property.start,
}); });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
start: node.end - 2, start: node.end - 2,
end: node.end, end: node.end,
@ -193,14 +283,14 @@ function unwrap(ctx) {
break; break;
case '$get': case '$get':
case 'rewriteUrl': case 'rewriteUrl':
const [ arg ] = node.arguments; const [arg] = node.arguments;
data.changes.push({ data.changes.push({
start: node.callee.start, start: node.callee.start,
end: arg.start, end: arg.start,
}); });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
start: node.end - 1, start: node.end - 1,
end: node.end, end: node.end,
@ -208,36 +298,43 @@ function unwrap(ctx) {
}; };
break; break;
case 'rewrite': case 'rewrite':
const [ script ] = node.arguments; const [script] = node.arguments;
data.changes.push({ data.changes.push({
start: node.callee.start, start: node.callee.start,
end: script.start, end: script.start,
}); });
node.iterateEnd = function() { node.iterateEnd = function () {
data.changes.push({ data.changes.push({
start: node.end - 1, start: node.end - 1,
end: node.end, end: node.end,
}); });
}; };
}; }
}); });
}; }
function isWrapped(node) { function isWrapped(node) {
if (node.type !== Syntax.MemberExpression) return false; if (node.type !== Syntax.MemberExpression) return false;
if (node.property.name === 'rewrite' && isWrapped(node.object)) return true; if (node.property.name === 'rewrite' && isWrapped(node.object)) return true;
if (node.object.type !== Syntax.Identifier || node.object.name !== '__uv') return false; if (node.object.type !== Syntax.Identifier || node.object.name !== '__uv')
if (!['js', '$get', '$wrap', 'rewriteUrl'].includes(node.property.name)) return false; return false;
if (!['js', '$get', '$wrap', 'rewriteUrl'].includes(node.property.name))
return false;
return true; return true;
}; }
function computedProperty(parent) { function computedProperty(parent) {
if (!parent.computed) return false; if (!parent.computed) return false;
const { property: node } = parent; const { property: node } = parent;
if (node.type === 'Literal' && !['location', 'top', 'parent']) return false; if (node.type === 'Literal' && !['location', 'top', 'parent']) return false;
return true; return true;
}
export {
property,
wrapEval,
dynamicImport,
importDeclaration,
identifier,
unwrap,
}; };
export { property, wrapEval, dynamicImport, importDeclaration, identifier, unwrap };

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,10 @@ class UVServiceWorker extends EventEmitter {
constructor(config = __uv$config) { constructor(config = __uv$config) {
super(); super();
if (!config.bare) config.bare = '/bare/'; if (!config.bare) config.bare = '/bare/';
this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location)); this.addresses =
typeof config.bare === 'string'
? [new URL(config.bare, location)]
: config.bare.map((str) => new URL(str, location));
this.headers = { this.headers = {
csp: [ csp: [
'cross-origin-embedder-policy', 'cross-origin-embedder-policy',
@ -25,82 +28,101 @@ class UVServiceWorker extends EventEmitter {
'x-powered-by', 'x-powered-by',
'x-xss-protection', 'x-xss-protection',
], ],
forward: [ forward: ['accept-encoding', 'connection', 'content-length'],
'accept-encoding',
'connection',
'content-length',
],
}; };
this.method = { this.method = {
empty: [ empty: ['GET', 'HEAD'],
'GET',
'HEAD'
]
}; };
this.statusCode = { this.statusCode = {
empty: [ empty: [204, 304],
204,
304,
],
}; };
this.config = config; this.config = config;
this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName(); this.browser = Ultraviolet.Bowser.getParser(
self.navigator.userAgent
).getBrowserName();
if (this.browser === 'Firefox') { if (this.browser === 'Firefox') {
this.headers.forward.push('user-agent'); this.headers.forward.push('user-agent');
this.headers.forward.push('content-type'); this.headers.forward.push('content-type');
}; }
}; }
async fetch({ request }) { async fetch({ request }) {
if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) { if (
!request.url.startsWith(
location.origin + (this.config.prefix || '/service/')
)
) {
return fetch(request); return fetch(request);
}; }
try { try {
const ultraviolet = new Ultraviolet(this.config); const ultraviolet = new Ultraviolet(this.config);
if (typeof this.config.construct === 'function') { if (typeof this.config.construct === 'function') {
this.config.construct(ultraviolet, 'service'); this.config.construct(ultraviolet, 'service');
}; }
const db = await ultraviolet.cookie.db(); const db = await ultraviolet.cookie.db();
ultraviolet.meta.origin = location.origin; ultraviolet.meta.origin = location.origin;
ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url)); ultraviolet.meta.base = ultraviolet.meta.url = new URL(
ultraviolet.sourceUrl(request.url)
);
const requestCtx = new RequestContext( const requestCtx = new RequestContext(
request, request,
this, this,
ultraviolet, ultraviolet,
!this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null !this.method.empty.includes(request.method.toUpperCase())
? await request.blob()
: null
); );
if (ultraviolet.meta.url.protocol === 'blob:') { if (ultraviolet.meta.url.protocol === 'blob:') {
requestCtx.blob = true; requestCtx.blob = true;
requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname); requestCtx.base = requestCtx.url = new URL(
}; requestCtx.url.pathname
);
}
if (request.referrer && request.referrer.startsWith(location.origin)) { if (
const referer = new URL(ultraviolet.sourceUrl(request.referrer)); request.referrer &&
request.referrer.startsWith(location.origin)
) {
const referer = new URL(
ultraviolet.sourceUrl(request.referrer)
);
if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') { if (
requestCtx.headers.origin ||
(ultraviolet.meta.url.origin !== referer.origin &&
request.mode === 'cors')
) {
requestCtx.headers.origin = referer.origin; requestCtx.headers.origin = referer.origin;
}; }
requestCtx.headers.referer = referer.href; requestCtx.headers.referer = referer.href;
}; }
const cookies = await ultraviolet.cookie.getCookies(db) || []; const cookies = (await ultraviolet.cookie.getCookies(db)) || [];
const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false); const cookieStr = ultraviolet.cookie.serialize(
cookies,
ultraviolet.meta,
false
);
if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) { if (
this.browser === 'Firefox' &&
!(
request.destination === 'iframe' ||
request.destination === 'document'
)
) {
requestCtx.forward.shift(); requestCtx.forward.shift();
}; }
if (cookieStr) requestCtx.headers.cookie = cookieStr; if (cookieStr) requestCtx.headers.cookie = cookieStr;
requestCtx.headers.Host = requestCtx.url.host; requestCtx.headers.Host = requestCtx.url.host;
const reqEvent = new HookEvent(requestCtx, null, null); const reqEvent = new HookEvent(requestCtx, null, null);
this.emit('request', reqEvent); this.emit('request', reqEvent);
@ -110,7 +132,7 @@ class UVServiceWorker extends EventEmitter {
if (response.status === 500) { if (response.status === 500) {
return Promise.reject(''); return Promise.reject('');
}; }
const responseCtx = new ResponseContext(requestCtx, response, this); const responseCtx = new ResponseContext(requestCtx, response, this);
const resEvent = new HookEvent(responseCtx, null, null); const resEvent = new HookEvent(responseCtx, null, null);
@ -120,16 +142,24 @@ class UVServiceWorker extends EventEmitter {
for (const name of this.headers.csp) { for (const name of this.headers.csp) {
if (responseCtx.headers[name]) delete responseCtx.headers[name]; if (responseCtx.headers[name]) delete responseCtx.headers[name];
}; }
if (responseCtx.headers.location) { if (responseCtx.headers.location) {
responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location); responseCtx.headers.location = ultraviolet.rewriteUrl(
}; responseCtx.headers.location
);
}
if (responseCtx.headers['set-cookie']) { if (responseCtx.headers['set-cookie']) {
Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => { Promise.resolve(
self.clients.matchAll().then(function (clients){ ultraviolet.cookie.setCookies(
clients.forEach(function(client){ responseCtx.headers['set-cookie'],
db,
ultraviolet.meta
)
).then(() => {
self.clients.matchAll().then(function (clients) {
clients.forEach(function (client) {
client.postMessage({ client.postMessage({
msg: 'updateCookies', msg: 'updateCookies',
url: ultraviolet.meta.url.href, url: ultraviolet.meta.url.href,
@ -138,10 +168,10 @@ class UVServiceWorker extends EventEmitter {
}); });
}); });
delete responseCtx.headers['set-cookie']; delete responseCtx.headers['set-cookie'];
}; }
if (responseCtx.body) { if (responseCtx.body) {
switch(request.destination) { switch (request.destination) {
case 'script': case 'script':
case 'worker': case 'worker':
responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`; responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
@ -156,27 +186,36 @@ class UVServiceWorker extends EventEmitter {
break; break;
case 'iframe': case 'iframe':
case 'document': case 'document':
if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) { if (
isHtml(
ultraviolet.meta.url,
responseCtx.headers['content-type'] || ''
)
) {
responseCtx.body = ultraviolet.rewriteHtml( responseCtx.body = ultraviolet.rewriteHtml(
await response.text(), await response.text(),
{ {
document: true , document: true,
injectHead: ultraviolet.createHtmlInject( injectHead: ultraviolet.createHtmlInject(
this.config.handler, this.config.handler,
this.config.bundle, this.config.bundle,
this.config.config, this.config.config,
ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true), ultraviolet.cookie.serialize(
cookies,
ultraviolet.meta,
true
),
request.referrer request.referrer
) ),
} }
); );
}; }
}; }
}; }
if (requestCtx.headers.accept === 'text/event-stream') { if (requestCtx.headers.accept === 'text/event-stream') {
responseCtx.headers['content-type'] = 'text/event-stream'; responseCtx.headers['content-type'] = 'text/event-stream';
}; }
this.emit('response', resEvent); this.emit('response', resEvent);
if (resEvent.intercepted) return resEvent.returnValue; if (resEvent.intercepted) return resEvent.returnValue;
@ -186,40 +225,46 @@ class UVServiceWorker extends EventEmitter {
status: responseCtx.status, status: responseCtx.status,
statusText: responseCtx.statusText, statusText: responseCtx.statusText,
}); });
} catch (err) {
} catch(err) {
return new Response(err.toString(), { return new Response(err.toString(), {
status: 500, status: 500,
}); });
}; }
}; }
getBarerResponse(response) { getBarerResponse(response) {
const headers = {}; const headers = {};
const raw = JSON.parse(response.headers.get('x-bare-headers')); const raw = JSON.parse(response.headers.get('x-bare-headers'));
for (const key in raw) { for (const key in raw) {
headers[key.toLowerCase()] = raw[key]; headers[key.toLowerCase()] = raw[key];
}; }
return { return {
headers, headers,
status: +response.headers.get('x-bare-status'), status: +response.headers.get('x-bare-status'),
statusText: response.headers.get('x-bare-status-text'), statusText: response.headers.get('x-bare-status-text'),
body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null, body: !this.statusCode.empty.includes(
}; +response.headers.get('x-bare-status')
)
? response.body
: null,
}; };
}
get address() { get address() {
return this.addresses[Math.floor(Math.random() * this.addresses.length)]; return this.addresses[
}; Math.floor(Math.random() * this.addresses.length)
];
}
static Ultraviolet = Ultraviolet; static Ultraviolet = Ultraviolet;
}; }
self.UVServiceWorker = UVServiceWorker; self.UVServiceWorker = UVServiceWorker;
class ResponseContext { class ResponseContext {
constructor(request, response, worker) { constructor(request, response, worker) {
const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : { const { headers, status, statusText, body } = !request.blob
? worker.getBarerResponse(response)
: {
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
headers: Object.fromEntries([...response.headers.entries()]), headers: Object.fromEntries([...response.headers.entries()]),
@ -232,17 +277,17 @@ class ResponseContext {
this.status = status; this.status = status;
this.statusText = statusText; this.statusText = statusText;
this.body = body; this.body = body;
}; }
get url() { get url() {
return this.request.url; return this.request.url;
} }
get base() { get base() {
return this.request.base; return this.request.base;
}; }
set base(val) { set base(val) {
this.request.base = val; this.request.base = val;
}; }
}; }
class RequestContext { class RequestContext {
constructor(request, worker, ultraviolet, body = null) { constructor(request, worker, ultraviolet, body = null) {
@ -257,41 +302,56 @@ class RequestContext {
this.credentials = 'omit'; this.credentials = 'omit';
this.mode = request.mode === 'cors' ? request.mode : 'same-origin'; this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
this.blob = false; this.blob = false;
}; }
get send() { get send() {
return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), { return new Request(
!this.blob
? this.address.href + 'v1/'
: 'blob:' + location.origin + this.url.pathname,
{
method: this.method, method: this.method,
headers: { headers: {
'x-bare-protocol': this.url.protocol, 'x-bare-protocol': this.url.protocol,
'x-bare-host': this.url.hostname, 'x-bare-host': this.url.hostname,
'x-bare-path': this.url.pathname + this.url.search, 'x-bare-path': this.url.pathname + this.url.search,
'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'), 'x-bare-port':
this.url.port ||
(this.url.protocol === 'https:' ? '443' : '80'),
'x-bare-headers': JSON.stringify(this.headers), 'x-bare-headers': JSON.stringify(this.headers),
'x-bare-forward-headers': JSON.stringify(this.forward), 'x-bare-forward-headers': JSON.stringify(this.forward),
}, },
redirect: this.redirect, redirect: this.redirect,
credentials: this.credentials, credentials: this.credentials,
mode: location.origin !== this.address.origin ? 'cors' : this.mode, mode:
body: this.body location.origin !== this.address.origin
}); ? 'cors'
}; : this.mode,
body: this.body,
}
);
}
get url() { get url() {
return this.ultraviolet.meta.url; return this.ultraviolet.meta.url;
}; }
set url(val) { set url(val) {
this.ultraviolet.meta.url = val; this.ultraviolet.meta.url = val;
}; }
get base() { get base() {
return this.ultraviolet.meta.base; return this.ultraviolet.meta.base;
}; }
set base(val) { set base(val) {
this.ultraviolet.meta.base = val; this.ultraviolet.meta.base = val;
}; }
} }
function isHtml(url, contentType = '') { function isHtml(url, contentType = '') {
return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html'; return (
}; (
Ultraviolet.mime.contentType(contentType || url.pathname) ||
'text/html'
).split(';')[0] === 'text/html'
);
}
class HookEvent { class HookEvent {
#intercepted; #intercepted;
@ -302,33 +362,35 @@ class HookEvent {
this.data = data; this.data = data;
this.target = target; this.target = target;
this.that = that; this.that = that;
}; }
get intercepted() { get intercepted() {
return this.#intercepted; return this.#intercepted;
}; }
get returnValue() { get returnValue() {
return this.#returnValue; return this.#returnValue;
}; }
respondWith(input) { respondWith(input) {
this.#returnValue = input; this.#returnValue = input;
this.#intercepted = true; this.#intercepted = true;
}; }
}; }
var R = typeof Reflect === 'object' ? Reflect : null var R = typeof Reflect === 'object' ? Reflect : null;
var ReflectApply = R && typeof R.apply === 'function' var ReflectApply =
R && typeof R.apply === 'function'
? R.apply ? R.apply
: function ReflectApply(target, receiver, args) { : function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args); return Function.prototype.apply.call(target, receiver, args);
} };
var ReflectOwnKeys var ReflectOwnKeys;
if (R && typeof R.ownKeys === 'function') { if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys ReflectOwnKeys = R.ownKeys;
} else if (Object.getOwnPropertySymbols) { } else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) { ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target) return Object.getOwnPropertyNames(target).concat(
.concat(Object.getOwnPropertySymbols(target)); Object.getOwnPropertySymbols(target)
);
}; };
} else { } else {
ReflectOwnKeys = function ReflectOwnKeys(target) { ReflectOwnKeys = function ReflectOwnKeys(target) {
@ -340,9 +402,11 @@ function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning); if (console && console.warn) console.warn(warning);
} }
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { var NumberIsNaN =
Number.isNaN ||
function NumberIsNaN(value) {
return value !== value; return value !== value;
} };
function EventEmitter() { function EventEmitter() {
EventEmitter.init.call(this); EventEmitter.init.call(this);
@ -361,27 +425,35 @@ var defaultMaxListeners = 10;
function checkListener(listener) { function checkListener(listener) {
if (typeof listener !== 'function') { if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); throw new TypeError(
'The "listener" argument must be of type Function. Received type ' +
typeof listener
);
} }
} }
Object.defineProperty(EventEmitter, 'defaultMaxListeners', { Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true, enumerable: true,
get: function() { get: function () {
return defaultMaxListeners; return defaultMaxListeners;
}, },
set: function(arg) { set: function (arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); throw new RangeError(
'The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' +
arg +
'.'
);
} }
defaultMaxListeners = arg; defaultMaxListeners = arg;
} },
}); });
EventEmitter.init = function() { EventEmitter.init = function () {
if (
if (this._events === undefined || this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) { this._events === Object.getPrototypeOf(this)._events
) {
this._events = Object.create(null); this._events = Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} }
@ -393,7 +465,11 @@ EventEmitter.init = function() {
// that to be increased. Set to zero for unlimited. // that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); throw new RangeError(
'The value of "n" is out of range. It must be a non-negative number. Received ' +
n +
'.'
);
} }
this._maxListeners = n; this._maxListeners = n;
return this; return this;
@ -412,42 +488,39 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
EventEmitter.prototype.emit = function emit(type) { EventEmitter.prototype.emit = function emit(type) {
var args = []; var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error'); var doError = type === 'error';
var events = this._events; var events = this._events;
if (events !== undefined) if (events !== undefined) doError = doError && events.error === undefined;
doError = (doError && events.error === undefined); else if (!doError) return false;
else if (!doError)
return false;
// If there is no 'error' event listener then throw. // If there is no 'error' event listener then throw.
if (doError) { if (doError) {
var er; var er;
if (args.length > 0) if (args.length > 0) er = args[0];
er = args[0];
if (er instanceof Error) { if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show // Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception. // up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event throw er; // Unhandled 'error' event
} }
// At least give some kind of context to the user // At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); var err = new Error(
'Unhandled error.' + (er ? ' (' + er.message + ')' : '')
);
err.context = er; err.context = er;
throw err; // Unhandled 'error' event throw err; // Unhandled 'error' event
} }
var handler = events[type]; var handler = events[type];
if (handler === undefined) if (handler === undefined) return false;
return false;
if (typeof handler === 'function') { if (typeof handler === 'function') {
ReflectApply(handler, this, args); ReflectApply(handler, this, args);
} else { } else {
var len = handler.length; var len = handler.length;
var listeners = arrayClone(handler, len); var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i) for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
ReflectApply(listeners[i], this, args);
} }
return true; return true;
@ -468,8 +541,11 @@ function _addListener(target, type, listener, prepend) {
// To avoid recursion in the case that type === "newListener"! Before // To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener". // adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) { if (events.newListener !== undefined) {
target.emit('newListener', type, target.emit(
listener.listener ? listener.listener : listener); 'newListener',
type,
listener.listener ? listener.listener : listener
);
// Re-assign `events` because a newListener handler could have caused the // Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object // this._events to be assigned to a new object
@ -485,8 +561,9 @@ function _addListener(target, type, listener, prepend) {
} else { } else {
if (typeof existing === 'function') { if (typeof existing === 'function') {
// Adding the second element, need to change to array. // Adding the second element, need to change to array.
existing = events[type] = existing = events[type] = prepend
prepend ? [listener, existing] : [existing, listener]; ? [listener, existing]
: [existing, listener];
// If we've already got an array, just append. // If we've already got an array, just append.
} else if (prepend) { } else if (prepend) {
existing.unshift(listener); existing.unshift(listener);
@ -500,10 +577,15 @@ function _addListener(target, type, listener, prepend) {
existing.warned = true; existing.warned = true;
// No error code for this since it is a Warning // No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' + var w = new Error(
existing.length + ' ' + String(type) + ' listeners ' + 'Possible EventEmitter memory leak detected. ' +
existing.length +
' ' +
String(type) +
' listeners ' +
'added. Use emitter.setMaxListeners() to ' + 'added. Use emitter.setMaxListeners() to ' +
'increase limit'); 'increase limit'
);
w.name = 'MaxListenersExceededWarning'; w.name = 'MaxListenersExceededWarning';
w.emitter = target; w.emitter = target;
w.type = type; w.type = type;
@ -521,23 +603,30 @@ EventEmitter.prototype.addListener = function addListener(type, listener) {
EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener = EventEmitter.prototype.prependListener = function prependListener(
function prependListener(type, listener) { type,
listener
) {
return _addListener(this, type, listener, true); return _addListener(this, type, listener, true);
}; };
function onceWrapper() { function onceWrapper() {
if (!this.fired) { if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn); this.target.removeListener(this.type, this.wrapFn);
this.fired = true; this.fired = true;
if (arguments.length === 0) if (arguments.length === 0) return this.listener.call(this.target);
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments); return this.listener.apply(this.target, arguments);
} }
} }
function _onceWrap(target, type, listener) { function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; var state = {
fired: false,
wrapFn: undefined,
target: target,
type: type,
listener: listener,
};
var wrapped = onceWrapper.bind(state); var wrapped = onceWrapper.bind(state);
wrapped.listener = listener; wrapped.listener = listener;
state.wrapFn = wrapped; state.wrapFn = wrapped;
@ -550,31 +639,32 @@ EventEmitter.prototype.once = function once(type, listener) {
return this; return this;
}; };
EventEmitter.prototype.prependOnceListener = EventEmitter.prototype.prependOnceListener = function prependOnceListener(
function prependOnceListener(type, listener) { type,
listener
) {
checkListener(listener); checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener)); this.prependListener(type, _onceWrap(this, type, listener));
return this; return this;
}; };
// Emits a 'removeListener' event if and only if the listener was removed. // Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener = EventEmitter.prototype.removeListener = function removeListener(
function removeListener(type, listener) { type,
listener
) {
var list, events, position, i, originalListener; var list, events, position, i, originalListener;
checkListener(listener); checkListener(listener);
events = this._events; events = this._events;
if (events === undefined) if (events === undefined) return this;
return this;
list = events[type]; list = events[type];
if (list === undefined) if (list === undefined) return this;
return this;
if (list === listener || list.listener === listener) { if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) if (--this._eventsCount === 0) this._events = Object.create(null);
this._events = Object.create(null);
else { else {
delete events[type]; delete events[type];
if (events.removeListener) if (events.removeListener)
@ -591,34 +681,29 @@ EventEmitter.prototype.removeListener =
} }
} }
if (position < 0) if (position < 0) return this;
return this;
if (position === 0) if (position === 0) list.shift();
list.shift();
else { else {
spliceOne(list, position); spliceOne(list, position);
} }
if (list.length === 1) if (list.length === 1) events[type] = list[0];
events[type] = list[0];
if (events.removeListener !== undefined) if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener); this.emit('removeListener', type, originalListener || listener);
} }
return this; return this;
}; };
EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners = EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
function removeAllListeners(type) {
var listeners, events, i; var listeners, events, i;
events = this._events; events = this._events;
if (events === undefined) if (events === undefined) return this;
return this;
// not listening for removeListener, no need to emit // not listening for removeListener, no need to emit
if (events.removeListener === undefined) { if (events.removeListener === undefined) {
@ -626,10 +711,8 @@ EventEmitter.prototype.removeAllListeners =
this._events = Object.create(null); this._events = Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} else if (events[type] !== undefined) { } else if (events[type] !== undefined) {
if (--this._eventsCount === 0) if (--this._eventsCount === 0) this._events = Object.create(null);
this._events = Object.create(null); else delete events[type];
else
delete events[type];
} }
return this; return this;
} }
@ -661,23 +744,22 @@ EventEmitter.prototype.removeAllListeners =
} }
return this; return this;
}; };
function _listeners(target, type, unwrap) { function _listeners(target, type, unwrap) {
var events = target._events; var events = target._events;
if (events === undefined) if (events === undefined) return [];
return [];
var evlistener = events[type]; var evlistener = events[type];
if (evlistener === undefined) if (evlistener === undefined) return [];
return [];
if (typeof evlistener === 'function') if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ? return unwrap
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); ? unwrapListeners(evlistener)
: arrayClone(evlistener, evlistener.length);
} }
EventEmitter.prototype.listeners = function listeners(type) { EventEmitter.prototype.listeners = function listeners(type) {
@ -688,7 +770,7 @@ EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false); return _listeners(this, type, false);
}; };
EventEmitter.listenerCount = function(emitter, type) { EventEmitter.listenerCount = function (emitter, type) {
if (typeof emitter.listenerCount === 'function') { if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type); return emitter.listenerCount(type);
} else { } else {
@ -719,14 +801,12 @@ EventEmitter.prototype.eventNames = function eventNames() {
function arrayClone(arr, n) { function arrayClone(arr, n) {
var copy = new Array(n); var copy = new Array(n);
for (var i = 0; i < n; ++i) for (var i = 0; i < n; ++i) copy[i] = arr[i];
copy[i] = arr[i];
return copy; return copy;
} }
function spliceOne(list, index) { function spliceOne(list, index) {
for (; index + 1 < list.length; index++) for (; index + 1 < list.length; index++) list[index] = list[index + 1];
list[index] = list[index + 1];
list.pop(); list.pop();
} }
@ -750,11 +830,13 @@ function once(emitter, name) {
emitter.removeListener('error', errorListener); emitter.removeListener('error', errorListener);
} }
resolve([].slice.call(arguments)); resolve([].slice.call(arguments));
}; }
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== 'error') { if (name !== 'error') {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); addErrorHandlerIfEventEmitter(emitter, errorListener, {
once: true,
});
} }
}); });
} }
@ -784,6 +866,9 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
listener(arg); listener(arg);
}); });
} else { } else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); throw new TypeError(
'The "emitter" argument must be of type EventEmitter. Received type ' +
typeof emitter
);
} }
} }

View file

@ -11,7 +11,9 @@ const isDevelopment = process.env.NODE_ENV !== 'production';
const config = { const config = {
mode: isDevelopment ? 'development' : 'production', mode: isDevelopment ? 'development' : 'production',
entry: { entry: {
bundle: fileURLToPath(new URL('./src/rewrite/index.js', import.meta.url)), bundle: fileURLToPath(
new URL('./src/rewrite/index.js', import.meta.url)
),
handler: fileURLToPath(new URL('./src/uv.handler.js', import.meta.url)), handler: fileURLToPath(new URL('./src/uv.handler.js', import.meta.url)),
sw: fileURLToPath(new URL('./src/uv.sw.js', import.meta.url)), sw: fileURLToPath(new URL('./src/uv.sw.js', import.meta.url)),
}, },
@ -31,10 +33,14 @@ const config = {
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ {
from: fileURLToPath(new URL('./src/uv.config.js', import.meta.url)), from: fileURLToPath(
new URL('./src/uv.config.js', import.meta.url)
),
}, },
{ {
from: fileURLToPath(new URL('./src/sw.js', import.meta.url)), from: fileURLToPath(
new URL('./src/sw.js', import.meta.url)
),
}, },
], ],
}), }),