├── uv.png ├── uv.zip ├── sw.js ├── uv ├── uv.config.js ├── uv.sw.js └── uv.handler.js ├── README.md ├── index.js ├── index.css └── index.html /uv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Ultraviolet-Static-Archive/HEAD/uv.png -------------------------------------------------------------------------------- /uv.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/titaniumnetwork-dev/Ultraviolet-Static-Archive/HEAD/uv.zip -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | importScripts('./uv/uv.sw.js'); 2 | 3 | const sw = new UVServiceWorker(); 4 | 5 | self.addEventListener('fetch', event => 6 | event.respondWith( 7 | sw.fetch(event) 8 | ) 9 | ); -------------------------------------------------------------------------------- /uv/uv.config.js: -------------------------------------------------------------------------------- 1 | self.__uv$config = { 2 | prefix: '/service/', 3 | bare: '/bare/', 4 | encodeUrl: Ultraviolet.codec.xor.encode, 5 | decodeUrl: Ultraviolet.codec.xor.decode, 6 | handler: '/uv/uv.handler.js', 7 | bundle: '/uv/uv.bundle.js', 8 | config: '/uv/uv.config.js', 9 | sw: '/uv/uv.sw.js', 10 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ultraviolet-scripts 2 | Core Ultraviolet scripts 3 | 4 | # Configuration 5 | Configure Ultraviolet for both client-hooking & service worker in `uv.config.js` 6 | ```javascript 7 | self.__uv$config = { 8 | bare: '/bare/', 9 | prefix: '/service/', 10 | encodeUrl: Ultraviolet.codec.xor.encode, 11 | decodeUrl: Ultraviolet.codec.xor.decode, 12 | handler: '/uv.handler.js', 13 | bundle: '/uv.bundle.js', 14 | config: '/uv.config.js', 15 | }; 16 | ``` 17 | 18 | 19 | # Example Usage 20 | ```javascript 21 | importScripts('/PATHTOSCRIPTS/uv.sw.js'); 22 | 23 | const sw = new UVServiceWorker(); 24 | 25 | self.addEventListener('fetch', event => 26 | event.respondWith( 27 | sw.fetch(event) 28 | ) 29 | ); 30 | ``` 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const form = document.querySelector('form'); 2 | const input = document.querySelector('input'); 3 | 4 | form.addEventListener('submit', async event => { 5 | event.preventDefault(); 6 | window.navigator.serviceWorker.register('./sw.js', { 7 | scope: __uv$config.prefix 8 | }).then(() => { 9 | let url = input.value.trim(); 10 | if (!isUrl(url)) url = 'https://www.google.com/search?q=' + url; 11 | else if (!(url.startsWith('https://') || url.startsWith('http://'))) url = 'http://' + url; 12 | 13 | 14 | window.location.href = __uv$config.prefix + __uv$config.encodeUrl(url); 15 | }); 16 | }); 17 | 18 | function isUrl(val = ''){ 19 | if (/^http(s?):\/\//.test(val) || val.includes('.') && val.substr(0, 1) !== ' ') return true; 20 | return false; 21 | }; 22 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;1,300;1,400&display=swap'); 2 | @import url('https://unpkg.com/@fortawesome/fontawesome-free@5.15.4/css/all.min.css'); 3 | @keyframes fadeInAnimation { 4 | 0% { 5 | opacity: 0; 6 | } 7 | 100% { 8 | opacity: 1; 9 | } 10 | } 11 | 12 | * { 13 | font-family: Roboto; 14 | } 15 | 16 | body { 17 | margin: 0; 18 | background: #111; 19 | display: flex; 20 | flex-direction: column; 21 | min-height: 100vh; 22 | animation: fadeInAnimation ease-in-out 0.3s; 23 | animation-iteration-count: 1; 24 | animation-fill-mode: forwards; 25 | } 26 | 27 | .fa-magnifying-glass { 28 | color: white; 29 | } 30 | 31 | .logo-wrapper { 32 | display: flex; 33 | align-items: center; 34 | justify-content: center; 35 | flex-direction: column; 36 | margin-top: 10%; 37 | } 38 | 39 | .logo { 40 | width: 150px; 41 | } 42 | 43 | .logo-wrapper .text { 44 | font-size: 75px; 45 | font-family: "Roboto"; 46 | color: #FFF; 47 | } 48 | 49 | .logo-wrapper h1 { 50 | color: white; 51 | } 52 | 53 | footer { 54 | margin-top: auto; 55 | width: 93%; 56 | align-self: center; 57 | height: 80px; 58 | display: flex; 59 | justify-content: left; 60 | align-items: center; 61 | } 62 | 63 | footer a, 64 | footer span { 65 | margin: 0 15px; 66 | text-decoration: none; 67 | color: #FFF; 68 | font-size: 15px; 69 | } 70 | 71 | footer a { 72 | cursor: pointer; 73 | } 74 | 75 | footer a:hover { 76 | text-decoration: underline; 77 | } 78 | 79 | form { 80 | display: flex; 81 | justify-content: center; 82 | } 83 | 84 | .desc { 85 | display: flex; 86 | justify-content: center; 87 | } 88 | 89 | .desc p { 90 | width: 560px; 91 | color: rgba(253, 253, 253, 0.514); 92 | } 93 | 94 | form input { 95 | background: none; 96 | font-family: inherit; 97 | padding: 0px 17px; 98 | height: 48px; 99 | border: 1px solid rgb(255, 255, 255, .2); 100 | color: var(--text-color); 101 | border-radius: 3px; 102 | outline: none; 103 | width: 350px; 104 | margin-top: 5px; 105 | border-radius: 50px; 106 | color: #FFF; 107 | } 108 | 109 | form input:focus { 110 | border: 1px solid rgba(253, 253, 253, 0.514); 111 | border-radius: 6px; 112 | animation: fadeInAnimation ease-in-out 0.3s; 113 | animation-iteration-count: 1; 114 | animation-fill-mode: forwards; 115 | } -------------------------------------------------------------------------------- /uv/uv.sw.js: -------------------------------------------------------------------------------- 1 | importScripts('/uv/uv.bundle.js'); 2 | importScripts('/uv/uv.config.js'); 3 | 4 | class UVServiceWorker extends EventEmitter { 5 | constructor(config = __uv$config) { 6 | super(); 7 | if (!config.bare) config.bare = '/bare/'; 8 | this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location)); 9 | this.headers = { 10 | csp: [ 11 | 'cross-origin-embedder-policy', 12 | 'cross-origin-opener-policy', 13 | 'cross-origin-resource-policy', 14 | 'content-security-policy', 15 | 'content-security-policy-report-only', 16 | 'expect-ct', 17 | 'feature-policy', 18 | 'origin-isolation', 19 | 'strict-transport-security', 20 | 'upgrade-insecure-requests', 21 | 'x-content-type-options', 22 | 'x-download-options', 23 | 'x-frame-options', 24 | 'x-permitted-cross-domain-policies', 25 | 'x-powered-by', 26 | 'x-xss-protection', 27 | ], 28 | forward: [ 29 | 'accept-encoding', 30 | 'connection', 31 | 'content-length', 32 | ], 33 | }; 34 | this.method = { 35 | empty: [ 36 | 'GET', 37 | 'HEAD' 38 | ] 39 | }; 40 | this.statusCode = { 41 | empty: [ 42 | 204, 43 | 304, 44 | ], 45 | }; 46 | this.config = config; 47 | this.browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName(); 48 | 49 | if (this.browser === 'Firefox') { 50 | this.headers.forward.push('user-agent'); 51 | this.headers.forward.push('content-type'); 52 | }; 53 | }; 54 | async fetch({ request }) { 55 | if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) { 56 | return fetch(request); 57 | }; 58 | try { 59 | 60 | const ultraviolet = new Ultraviolet(this.config); 61 | 62 | if (typeof this.config.construct === 'function') { 63 | this.config.construct(ultraviolet, 'service'); 64 | }; 65 | 66 | const db = await ultraviolet.cookie.db(); 67 | 68 | ultraviolet.meta.origin = location.origin; 69 | ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url)); 70 | 71 | const requestCtx = new RequestContext( 72 | request, 73 | this, 74 | ultraviolet, 75 | !this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null 76 | ); 77 | 78 | if (ultraviolet.meta.url.protocol === 'blob:') { 79 | requestCtx.blob = true; 80 | requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname); 81 | }; 82 | 83 | if (request.referrer && request.referrer.startsWith(location.origin)) { 84 | const referer = new URL(ultraviolet.sourceUrl(request.referrer)); 85 | 86 | if (requestCtx.headers.origin || ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') { 87 | requestCtx.headers.origin = referer.origin; 88 | }; 89 | 90 | requestCtx.headers.referer = referer.href; 91 | }; 92 | 93 | const cookies = await ultraviolet.cookie.getCookies(db) || []; 94 | const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false); 95 | 96 | if (this.browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) { 97 | requestCtx.forward.shift(); 98 | }; 99 | 100 | if (cookieStr) requestCtx.headers.cookie = cookieStr; 101 | requestCtx.headers.Host = requestCtx.url.host; 102 | 103 | 104 | const reqEvent = new HookEvent(requestCtx, null, null); 105 | this.emit('request', reqEvent); 106 | 107 | if (reqEvent.intercepted) return reqEvent.returnValue; 108 | 109 | const response = await fetch(requestCtx.send); 110 | 111 | if (response.status === 500) { 112 | return Promise.reject(''); 113 | }; 114 | 115 | const responseCtx = new ResponseContext(requestCtx, response, this); 116 | const resEvent = new HookEvent(responseCtx, null, null); 117 | 118 | this.emit('beforemod', resEvent); 119 | if (resEvent.intercepted) return resEvent.returnValue; 120 | 121 | for (const name of this.headers.csp) { 122 | if (responseCtx.headers[name]) delete responseCtx.headers[name]; 123 | }; 124 | 125 | if (responseCtx.headers.location) { 126 | responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location); 127 | }; 128 | 129 | if (responseCtx.headers['set-cookie']) { 130 | Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => { 131 | self.clients.matchAll().then(function (clients){ 132 | clients.forEach(function(client){ 133 | client.postMessage({ 134 | msg: 'updateCookies', 135 | url: ultraviolet.meta.url.href, 136 | }); 137 | }); 138 | }); 139 | }); 140 | delete responseCtx.headers['set-cookie']; 141 | }; 142 | 143 | if (responseCtx.body) { 144 | switch(request.destination) { 145 | case 'script': 146 | case 'worker': 147 | responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`; 148 | responseCtx.body += ultraviolet.js.rewrite( 149 | await response.text() 150 | ); 151 | break; 152 | case 'style': 153 | responseCtx.body = ultraviolet.rewriteCSS( 154 | await response.text() 155 | ); 156 | break; 157 | case 'iframe': 158 | case 'document': 159 | if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) { 160 | responseCtx.body = ultraviolet.rewriteHtml( 161 | await response.text(), 162 | { 163 | document: true , 164 | injectHead: ultraviolet.createHtmlInject( 165 | this.config.handler, 166 | this.config.bundle, 167 | this.config.config, 168 | ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true), 169 | request.referrer 170 | ) 171 | } 172 | ); 173 | }; 174 | }; 175 | }; 176 | 177 | if (requestCtx.headers.accept === 'text/event-stream') { 178 | responseCtx.headers['content-type'] = 'text/event-stream'; 179 | }; 180 | 181 | this.emit('response', resEvent); 182 | if (resEvent.intercepted) return resEvent.returnValue; 183 | 184 | return new Response(responseCtx.body, { 185 | headers: responseCtx.headers, 186 | status: responseCtx.status, 187 | statusText: responseCtx.statusText, 188 | }); 189 | 190 | } catch(err) { 191 | return new Response(err.toString(), { 192 | status: 500, 193 | }); 194 | }; 195 | }; 196 | getBarerResponse(response) { 197 | const headers = {}; 198 | const raw = JSON.parse(response.headers.get('x-bare-headers')); 199 | 200 | for (const key in raw) { 201 | headers[key.toLowerCase()] = raw[key]; 202 | }; 203 | 204 | return { 205 | headers, 206 | status: +response.headers.get('x-bare-status'), 207 | statusText: response.headers.get('x-bare-status-text'), 208 | body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null, 209 | }; 210 | }; 211 | get address() { 212 | return this.addresses[Math.floor(Math.random() * this.addresses.length)]; 213 | }; 214 | static Ultraviolet = Ultraviolet; 215 | }; 216 | 217 | self.UVServiceWorker = UVServiceWorker; 218 | 219 | 220 | class ResponseContext { 221 | constructor(request, response, worker) { 222 | const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : { 223 | status: response.status, 224 | statusText: response.statusText, 225 | headers: Object.fromEntries([...response.headers.entries()]), 226 | body: response.body, 227 | }; 228 | this.request = request; 229 | this.raw = response; 230 | this.ultraviolet = request.ultraviolet; 231 | this.headers = headers; 232 | this.status = status; 233 | this.statusText = statusText; 234 | this.body = body; 235 | }; 236 | get url() { 237 | return this.request.url; 238 | } 239 | get base() { 240 | return this.request.base; 241 | }; 242 | set base(val) { 243 | this.request.base = val; 244 | }; 245 | }; 246 | 247 | class RequestContext { 248 | constructor(request, worker, ultraviolet, body = null) { 249 | this.ultraviolet = ultraviolet; 250 | this.request = request; 251 | this.headers = Object.fromEntries([...request.headers.entries()]); 252 | this.method = request.method; 253 | this.forward = [...worker.headers.forward]; 254 | this.address = worker.address; 255 | this.body = body || null; 256 | this.redirect = request.redirect; 257 | this.credentials = 'omit'; 258 | this.mode = request.mode === 'cors' ? request.mode : 'same-origin'; 259 | this.blob = false; 260 | }; 261 | get send() { 262 | return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), { 263 | method: this.method, 264 | headers: { 265 | 'x-bare-protocol': this.url.protocol, 266 | 'x-bare-host': this.url.hostname, 267 | 'x-bare-path': this.url.pathname + this.url.search, 268 | 'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'), 269 | 'x-bare-headers': JSON.stringify(this.headers), 270 | 'x-bare-forward-headers': JSON.stringify(this.forward), 271 | }, 272 | redirect: this.redirect, 273 | credentials: this.credentials, 274 | mode: location.origin !== this.address.origin ? 'cors' : this.mode, 275 | body: this.body 276 | }); 277 | }; 278 | get url() { 279 | return this.ultraviolet.meta.url; 280 | }; 281 | set url(val) { 282 | this.ultraviolet.meta.url = val; 283 | }; 284 | get base() { 285 | return this.ultraviolet.meta.base; 286 | }; 287 | set base(val) { 288 | this.ultraviolet.meta.base = val; 289 | }; 290 | } 291 | 292 | function isHtml(url, contentType = '') { 293 | return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html'; 294 | }; 295 | 296 | class HookEvent { 297 | #intercepted; 298 | #returnValue; 299 | constructor(data = {}, target = null, that = null) { 300 | this.#intercepted = false; 301 | this.#returnValue = null; 302 | this.data = data; 303 | this.target = target; 304 | this.that = that; 305 | }; 306 | get intercepted() { 307 | return this.#intercepted; 308 | }; 309 | get returnValue() { 310 | return this.#returnValue; 311 | }; 312 | respondWith(input) { 313 | this.#returnValue = input; 314 | this.#intercepted = true; 315 | }; 316 | }; 317 | 318 | var R = typeof Reflect === 'object' ? Reflect : null 319 | var ReflectApply = R && typeof R.apply === 'function' 320 | ? R.apply 321 | : function ReflectApply(target, receiver, args) { 322 | return Function.prototype.apply.call(target, receiver, args); 323 | } 324 | 325 | var ReflectOwnKeys 326 | if (R && typeof R.ownKeys === 'function') { 327 | ReflectOwnKeys = R.ownKeys 328 | } else if (Object.getOwnPropertySymbols) { 329 | ReflectOwnKeys = function ReflectOwnKeys(target) { 330 | return Object.getOwnPropertyNames(target) 331 | .concat(Object.getOwnPropertySymbols(target)); 332 | }; 333 | } else { 334 | ReflectOwnKeys = function ReflectOwnKeys(target) { 335 | return Object.getOwnPropertyNames(target); 336 | }; 337 | } 338 | 339 | function ProcessEmitWarning(warning) { 340 | if (console && console.warn) console.warn(warning); 341 | } 342 | 343 | var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) { 344 | return value !== value; 345 | } 346 | 347 | function EventEmitter() { 348 | EventEmitter.init.call(this); 349 | } 350 | 351 | // Backwards-compat with node 0.10.x 352 | EventEmitter.EventEmitter = EventEmitter; 353 | 354 | EventEmitter.prototype._events = undefined; 355 | EventEmitter.prototype._eventsCount = 0; 356 | EventEmitter.prototype._maxListeners = undefined; 357 | 358 | // By default EventEmitters will print a warning if more than 10 listeners are 359 | // added to it. This is a useful default which helps finding memory leaks. 360 | var defaultMaxListeners = 10; 361 | 362 | function checkListener(listener) { 363 | if (typeof listener !== 'function') { 364 | throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); 365 | } 366 | } 367 | 368 | Object.defineProperty(EventEmitter, 'defaultMaxListeners', { 369 | enumerable: true, 370 | get: function() { 371 | return defaultMaxListeners; 372 | }, 373 | set: function(arg) { 374 | if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { 375 | throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.'); 376 | } 377 | defaultMaxListeners = arg; 378 | } 379 | }); 380 | 381 | EventEmitter.init = function() { 382 | 383 | if (this._events === undefined || 384 | this._events === Object.getPrototypeOf(this)._events) { 385 | this._events = Object.create(null); 386 | this._eventsCount = 0; 387 | } 388 | 389 | this._maxListeners = this._maxListeners || undefined; 390 | }; 391 | 392 | // Obviously not all Emitters should be limited to 10. This function allows 393 | // that to be increased. Set to zero for unlimited. 394 | EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { 395 | if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { 396 | throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); 397 | } 398 | this._maxListeners = n; 399 | return this; 400 | }; 401 | 402 | function _getMaxListeners(that) { 403 | if (that._maxListeners === undefined) 404 | return EventEmitter.defaultMaxListeners; 405 | return that._maxListeners; 406 | } 407 | 408 | EventEmitter.prototype.getMaxListeners = function getMaxListeners() { 409 | return _getMaxListeners(this); 410 | }; 411 | 412 | EventEmitter.prototype.emit = function emit(type) { 413 | var args = []; 414 | for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); 415 | var doError = (type === 'error'); 416 | 417 | var events = this._events; 418 | if (events !== undefined) 419 | doError = (doError && events.error === undefined); 420 | else if (!doError) 421 | return false; 422 | 423 | // If there is no 'error' event listener then throw. 424 | if (doError) { 425 | var er; 426 | if (args.length > 0) 427 | er = args[0]; 428 | if (er instanceof Error) { 429 | // Note: The comments on the `throw` lines are intentional, they show 430 | // up in Node's output if this results in an unhandled exception. 431 | throw er; // Unhandled 'error' event 432 | } 433 | // At least give some kind of context to the user 434 | var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : '')); 435 | err.context = er; 436 | throw err; // Unhandled 'error' event 437 | } 438 | 439 | var handler = events[type]; 440 | 441 | if (handler === undefined) 442 | return false; 443 | 444 | if (typeof handler === 'function') { 445 | ReflectApply(handler, this, args); 446 | } else { 447 | var len = handler.length; 448 | var listeners = arrayClone(handler, len); 449 | for (var i = 0; i < len; ++i) 450 | ReflectApply(listeners[i], this, args); 451 | } 452 | 453 | return true; 454 | }; 455 | 456 | function _addListener(target, type, listener, prepend) { 457 | var m; 458 | var events; 459 | var existing; 460 | 461 | checkListener(listener); 462 | 463 | events = target._events; 464 | if (events === undefined) { 465 | events = target._events = Object.create(null); 466 | target._eventsCount = 0; 467 | } else { 468 | // To avoid recursion in the case that type === "newListener"! Before 469 | // adding it to the listeners, first emit "newListener". 470 | if (events.newListener !== undefined) { 471 | target.emit('newListener', type, 472 | listener.listener ? listener.listener : listener); 473 | 474 | // Re-assign `events` because a newListener handler could have caused the 475 | // this._events to be assigned to a new object 476 | events = target._events; 477 | } 478 | existing = events[type]; 479 | } 480 | 481 | if (existing === undefined) { 482 | // Optimize the case of one listener. Don't need the extra array object. 483 | existing = events[type] = listener; 484 | ++target._eventsCount; 485 | } else { 486 | if (typeof existing === 'function') { 487 | // Adding the second element, need to change to array. 488 | existing = events[type] = 489 | prepend ? [listener, existing] : [existing, listener]; 490 | // If we've already got an array, just append. 491 | } else if (prepend) { 492 | existing.unshift(listener); 493 | } else { 494 | existing.push(listener); 495 | } 496 | 497 | // Check for listener leak 498 | m = _getMaxListeners(target); 499 | if (m > 0 && existing.length > m && !existing.warned) { 500 | existing.warned = true; 501 | // No error code for this since it is a Warning 502 | // eslint-disable-next-line no-restricted-syntax 503 | var w = new Error('Possible EventEmitter memory leak detected. ' + 504 | existing.length + ' ' + String(type) + ' listeners ' + 505 | 'added. Use emitter.setMaxListeners() to ' + 506 | 'increase limit'); 507 | w.name = 'MaxListenersExceededWarning'; 508 | w.emitter = target; 509 | w.type = type; 510 | w.count = existing.length; 511 | ProcessEmitWarning(w); 512 | } 513 | } 514 | 515 | return target; 516 | } 517 | 518 | EventEmitter.prototype.addListener = function addListener(type, listener) { 519 | return _addListener(this, type, listener, false); 520 | }; 521 | 522 | EventEmitter.prototype.on = EventEmitter.prototype.addListener; 523 | 524 | EventEmitter.prototype.prependListener = 525 | function prependListener(type, listener) { 526 | return _addListener(this, type, listener, true); 527 | }; 528 | 529 | function onceWrapper() { 530 | if (!this.fired) { 531 | this.target.removeListener(this.type, this.wrapFn); 532 | this.fired = true; 533 | if (arguments.length === 0) 534 | return this.listener.call(this.target); 535 | return this.listener.apply(this.target, arguments); 536 | } 537 | } 538 | 539 | function _onceWrap(target, type, listener) { 540 | var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; 541 | var wrapped = onceWrapper.bind(state); 542 | wrapped.listener = listener; 543 | state.wrapFn = wrapped; 544 | return wrapped; 545 | } 546 | 547 | EventEmitter.prototype.once = function once(type, listener) { 548 | checkListener(listener); 549 | this.on(type, _onceWrap(this, type, listener)); 550 | return this; 551 | }; 552 | 553 | EventEmitter.prototype.prependOnceListener = 554 | function prependOnceListener(type, listener) { 555 | checkListener(listener); 556 | this.prependListener(type, _onceWrap(this, type, listener)); 557 | return this; 558 | }; 559 | 560 | // Emits a 'removeListener' event if and only if the listener was removed. 561 | EventEmitter.prototype.removeListener = 562 | function removeListener(type, listener) { 563 | var list, events, position, i, originalListener; 564 | 565 | checkListener(listener); 566 | 567 | events = this._events; 568 | if (events === undefined) 569 | return this; 570 | 571 | list = events[type]; 572 | if (list === undefined) 573 | return this; 574 | 575 | if (list === listener || list.listener === listener) { 576 | if (--this._eventsCount === 0) 577 | this._events = Object.create(null); 578 | else { 579 | delete events[type]; 580 | if (events.removeListener) 581 | this.emit('removeListener', type, list.listener || listener); 582 | } 583 | } else if (typeof list !== 'function') { 584 | position = -1; 585 | 586 | for (i = list.length - 1; i >= 0; i--) { 587 | if (list[i] === listener || list[i].listener === listener) { 588 | originalListener = list[i].listener; 589 | position = i; 590 | break; 591 | } 592 | } 593 | 594 | if (position < 0) 595 | return this; 596 | 597 | if (position === 0) 598 | list.shift(); 599 | else { 600 | spliceOne(list, position); 601 | } 602 | 603 | if (list.length === 1) 604 | events[type] = list[0]; 605 | 606 | if (events.removeListener !== undefined) 607 | this.emit('removeListener', type, originalListener || listener); 608 | } 609 | 610 | return this; 611 | }; 612 | 613 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 614 | 615 | EventEmitter.prototype.removeAllListeners = 616 | function removeAllListeners(type) { 617 | var listeners, events, i; 618 | 619 | events = this._events; 620 | if (events === undefined) 621 | return this; 622 | 623 | // not listening for removeListener, no need to emit 624 | if (events.removeListener === undefined) { 625 | if (arguments.length === 0) { 626 | this._events = Object.create(null); 627 | this._eventsCount = 0; 628 | } else if (events[type] !== undefined) { 629 | if (--this._eventsCount === 0) 630 | this._events = Object.create(null); 631 | else 632 | delete events[type]; 633 | } 634 | return this; 635 | } 636 | 637 | // emit removeListener for all listeners on all events 638 | if (arguments.length === 0) { 639 | var keys = Object.keys(events); 640 | var key; 641 | for (i = 0; i < keys.length; ++i) { 642 | key = keys[i]; 643 | if (key === 'removeListener') continue; 644 | this.removeAllListeners(key); 645 | } 646 | this.removeAllListeners('removeListener'); 647 | this._events = Object.create(null); 648 | this._eventsCount = 0; 649 | return this; 650 | } 651 | 652 | listeners = events[type]; 653 | 654 | if (typeof listeners === 'function') { 655 | this.removeListener(type, listeners); 656 | } else if (listeners !== undefined) { 657 | // LIFO order 658 | for (i = listeners.length - 1; i >= 0; i--) { 659 | this.removeListener(type, listeners[i]); 660 | } 661 | } 662 | 663 | return this; 664 | }; 665 | 666 | function _listeners(target, type, unwrap) { 667 | var events = target._events; 668 | 669 | if (events === undefined) 670 | return []; 671 | 672 | var evlistener = events[type]; 673 | if (evlistener === undefined) 674 | return []; 675 | 676 | if (typeof evlistener === 'function') 677 | return unwrap ? [evlistener.listener || evlistener] : [evlistener]; 678 | 679 | return unwrap ? 680 | unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); 681 | } 682 | 683 | EventEmitter.prototype.listeners = function listeners(type) { 684 | return _listeners(this, type, true); 685 | }; 686 | 687 | EventEmitter.prototype.rawListeners = function rawListeners(type) { 688 | return _listeners(this, type, false); 689 | }; 690 | 691 | EventEmitter.listenerCount = function(emitter, type) { 692 | if (typeof emitter.listenerCount === 'function') { 693 | return emitter.listenerCount(type); 694 | } else { 695 | return listenerCount.call(emitter, type); 696 | } 697 | }; 698 | 699 | EventEmitter.prototype.listenerCount = listenerCount; 700 | function listenerCount(type) { 701 | var events = this._events; 702 | 703 | if (events !== undefined) { 704 | var evlistener = events[type]; 705 | 706 | if (typeof evlistener === 'function') { 707 | return 1; 708 | } else if (evlistener !== undefined) { 709 | return evlistener.length; 710 | } 711 | } 712 | 713 | return 0; 714 | } 715 | 716 | EventEmitter.prototype.eventNames = function eventNames() { 717 | return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; 718 | }; 719 | 720 | function arrayClone(arr, n) { 721 | var copy = new Array(n); 722 | for (var i = 0; i < n; ++i) 723 | copy[i] = arr[i]; 724 | return copy; 725 | } 726 | 727 | function spliceOne(list, index) { 728 | for (; index + 1 < list.length; index++) 729 | list[index] = list[index + 1]; 730 | list.pop(); 731 | } 732 | 733 | function unwrapListeners(arr) { 734 | var ret = new Array(arr.length); 735 | for (var i = 0; i < ret.length; ++i) { 736 | ret[i] = arr[i].listener || arr[i]; 737 | } 738 | return ret; 739 | } 740 | 741 | function once(emitter, name) { 742 | return new Promise(function (resolve, reject) { 743 | function errorListener(err) { 744 | emitter.removeListener(name, resolver); 745 | reject(err); 746 | } 747 | 748 | function resolver() { 749 | if (typeof emitter.removeListener === 'function') { 750 | emitter.removeListener('error', errorListener); 751 | } 752 | resolve([].slice.call(arguments)); 753 | }; 754 | 755 | eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); 756 | if (name !== 'error') { 757 | addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); 758 | } 759 | }); 760 | } 761 | 762 | function addErrorHandlerIfEventEmitter(emitter, handler, flags) { 763 | if (typeof emitter.on === 'function') { 764 | eventTargetAgnosticAddListener(emitter, 'error', handler, flags); 765 | } 766 | } 767 | 768 | function eventTargetAgnosticAddListener(emitter, name, listener, flags) { 769 | if (typeof emitter.on === 'function') { 770 | if (flags.once) { 771 | emitter.once(name, listener); 772 | } else { 773 | emitter.on(name, listener); 774 | } 775 | } else if (typeof emitter.addEventListener === 'function') { 776 | // EventTarget does not have `error` event semantics like Node 777 | // EventEmitters, we do not listen for `error` events here. 778 | emitter.addEventListener(name, function wrapListener(arg) { 779 | // IE does not have builtin `{ once: true }` support so we 780 | // have to do it manually. 781 | if (flags.once) { 782 | emitter.removeEventListener(name, wrapListener); 783 | } 784 | listener(arg); 785 | }); 786 | } else { 787 | throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); 788 | } 789 | } 790 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ultraviolet | Sophisticated Web Proxy 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 43 | 44 | 45 | 46 | 47 |
48 | 50 |

Ultraviolet | TN

51 |
52 |
53 |

Ultraviolet is highly sophisticated proxy used for evading internet censorship. 54 |

55 |
56 | 57 |
58 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /uv/uv.handler.js: -------------------------------------------------------------------------------- 1 | if (!self.__uv) { 2 | __uvHook(self, self.__uv$config, self.__uv$config.bare); 3 | }; 4 | 5 | async function __uvHook(window, config = {}, bare = '/bare/') { 6 | if ('__uv' in window && window.__uv instanceof Ultraviolet) return false; 7 | 8 | if (window.document && !!window.window) { 9 | window.document.querySelectorAll("script[__uv-script]").forEach(node => node.remove()) 10 | }; 11 | 12 | const worker = !window.window; 13 | const master = '__uv'; 14 | const methodPrefix = '__uv$'; 15 | const __uv = new Ultraviolet({ 16 | ...config, 17 | window, 18 | }); 19 | 20 | if (typeof config.construct === 'function') { 21 | config.construct(__uv, worker ? 'worker' : 'window'); 22 | }; 23 | 24 | const { client } = __uv; 25 | const { 26 | HTMLMediaElement, 27 | HTMLScriptElement, 28 | HTMLAudioElement, 29 | HTMLVideoElement, 30 | HTMLInputElement, 31 | HTMLEmbedElement, 32 | HTMLTrackElement, 33 | HTMLAnchorElement, 34 | HTMLIFrameElement, 35 | HTMLAreaElement, 36 | HTMLLinkElement, 37 | HTMLBaseElement, 38 | HTMLFormElement, 39 | HTMLImageElement, 40 | HTMLSourceElement, 41 | } = window; 42 | 43 | client.nativeMethods.defineProperty(window, '__uv', { 44 | value: __uv, 45 | enumerable: false, 46 | }); 47 | 48 | 49 | __uv.meta.origin = location.origin; 50 | __uv.location = client.location.emulate( 51 | (href) => { 52 | if (href === 'about:srcdoc') return new URL(href); 53 | if (href.startsWith('blob:')) href = href.slice('blob:'.length); 54 | return new URL(__uv.sourceUrl(href)); 55 | }, 56 | (href) => { 57 | return __uv.rewriteUrl(href); 58 | }, 59 | ); 60 | 61 | __uv.cookieStr = window.__uv$cookies || ''; 62 | __uv.meta.url = __uv.location; 63 | __uv.domain = __uv.meta.url.host; 64 | __uv.blobUrls = new window.Map(); 65 | __uv.referrer = ''; 66 | __uv.cookies = []; 67 | __uv.localStorageObj = {}; 68 | __uv.sessionStorageObj = {}; 69 | 70 | try { 71 | __uv.bare = new URL(bare, window.location.href); 72 | } catch(e) { 73 | __uv.bare = window.parent.__uv.bare; 74 | }; 75 | 76 | if (__uv.location.href === 'about:srcdoc') { 77 | __uv.meta = window.parent.__uv.meta; 78 | }; 79 | 80 | if (window.EventTarget) { 81 | __uv.addEventListener = window.EventTarget.prototype.addEventListener; 82 | __uv.removeListener = window.EventTarget.prototype.removeListener; 83 | __uv.dispatchEvent = window.EventTarget.prototype.dispatchEvent; 84 | }; 85 | 86 | // Storage wrappers 87 | client.nativeMethods.defineProperty(client.storage.storeProto, '__uv$storageObj', { 88 | get() { 89 | if (this === client.storage.sessionStorage) return __uv.sessionStorageObj; 90 | if (this === client.storage.localStorage) return __uv.localStorageObj; 91 | }, 92 | enumerable: false, 93 | }); 94 | 95 | if (window.localStorage) { 96 | for (const key in window.localStorage) { 97 | if (key.startsWith(methodPrefix + __uv.location.origin + '@')) { 98 | __uv.localStorageObj[key.slice((methodPrefix + __uv.location.origin + '@').length)] = window.localStorage.getItem(key); 99 | }; 100 | }; 101 | 102 | __uv.lsWrap = client.storage.emulate(client.storage.localStorage, __uv.localStorageObj); 103 | }; 104 | 105 | if (window.sessionStorage) { 106 | for (const key in window.sessionStorage) { 107 | if (key.startsWith(methodPrefix + __uv.location.origin + '@')) { 108 | __uv.sessionStorageObj[key.slice((methodPrefix + __uv.location.origin + '@').length)] = window.sessionStorage.getItem(key); 109 | }; 110 | }; 111 | 112 | __uv.ssWrap = client.storage.emulate(client.storage.sessionStorage, __uv.sessionStorageObj); 113 | }; 114 | 115 | 116 | 117 | let rawBase = window.document ? client.node.baseURI.get.call(window.document) : window.location.href; 118 | let base = __uv.sourceUrl(rawBase); 119 | 120 | client.nativeMethods.defineProperty(__uv.meta, 'base', { 121 | get() { 122 | if (!window.document) return __uv.meta.url.href; 123 | 124 | if (client.node.baseURI.get.call(window.document) !== rawBase) { 125 | rawBase = client.node.baseURI.get.call(window.document); 126 | base = __uv.sourceUrl(rawBase); 127 | }; 128 | 129 | return base; 130 | }, 131 | }); 132 | 133 | 134 | __uv.methods = { 135 | setSource: methodPrefix + 'setSource', 136 | source: methodPrefix + 'source', 137 | location: methodPrefix + 'location', 138 | function: methodPrefix + 'function', 139 | string: methodPrefix + 'string', 140 | eval: methodPrefix + 'eval', 141 | parent: methodPrefix + 'parent', 142 | top: methodPrefix + 'top', 143 | }; 144 | 145 | __uv.filterKeys = [ 146 | master, 147 | __uv.methods.setSource, 148 | __uv.methods.source, 149 | __uv.methods.location, 150 | __uv.methods.function, 151 | __uv.methods.string, 152 | __uv.methods.eval, 153 | __uv.methods.parent, 154 | __uv.methods.top, 155 | methodPrefix + 'protocol', 156 | methodPrefix + 'storageObj', 157 | methodPrefix + 'url', 158 | methodPrefix + 'modifiedStyle', 159 | methodPrefix + 'config', 160 | methodPrefix + 'dispatched', 161 | 'Ultraviolet', 162 | '__uvHook', 163 | ]; 164 | 165 | 166 | client.on('wrap', (target, wrapped) => { 167 | client.nativeMethods.defineProperty(wrapped, 'name', client.nativeMethods.getOwnPropertyDescriptor(target, 'name')); 168 | client.nativeMethods.defineProperty(wrapped, 'length', client.nativeMethods.getOwnPropertyDescriptor(target, 'length')); 169 | 170 | client.nativeMethods.defineProperty(wrapped, __uv.methods.string, { 171 | enumerable: false, 172 | value: client.nativeMethods.fnToString.call(target), 173 | }); 174 | 175 | client.nativeMethods.defineProperty(wrapped, __uv.methods.function, { 176 | enumerable: false, 177 | value: target, 178 | }); 179 | }); 180 | 181 | client.fetch.on('request', event => { 182 | event.data.input = __uv.rewriteUrl(event.data.input); 183 | }); 184 | 185 | client.fetch.on('requestUrl', event => { 186 | event.data.value = __uv.sourceUrl(event.data.value); 187 | }); 188 | 189 | client.fetch.on('responseUrl', event => { 190 | event.data.value = __uv.sourceUrl(event.data.value); 191 | }); 192 | 193 | // XMLHttpRequest 194 | client.xhr.on('open', event => { 195 | event.data.input = __uv.rewriteUrl(event.data.input); 196 | }); 197 | 198 | client.xhr.on('responseUrl', event => { 199 | event.data.value = __uv.sourceUrl(event.data.value); 200 | }); 201 | 202 | 203 | // Workers 204 | client.workers.on('worker', event => { 205 | event.data.url = __uv.rewriteUrl(event.data.url); 206 | }); 207 | 208 | client.workers.on('addModule', event => { 209 | event.data.url = __uv.rewriteUrl(event.data.url); 210 | }); 211 | 212 | client.workers.on('importScripts', event => { 213 | for (const i in event.data.scripts) { 214 | event.data.scripts[i] = __uv.rewriteUrl(event.data.scripts[i]); 215 | }; 216 | }); 217 | 218 | client.workers.on('postMessage', event => { 219 | let to = event.data.origin; 220 | 221 | event.data.origin = '*'; 222 | event.data.message = { 223 | __data: event.data.message, 224 | __origin: __uv.meta.url.origin, 225 | __to: to, 226 | }; 227 | }); 228 | 229 | // Navigator 230 | client.navigator.on('sendBeacon', event => { 231 | event.data.url = __uv.rewriteUrl(event.data.url); 232 | }); 233 | 234 | // Cookies 235 | client.document.on('getCookie', event => { 236 | event.data.value = __uv.cookieStr; 237 | }); 238 | 239 | client.document.on('setCookie', event => { 240 | Promise.resolve(__uv.cookie.setCookies(event.data.value, __uv.db, __uv.meta)).then(() => { 241 | __uv.cookie.db().then(db => { 242 | __uv.cookie.getCookies(db).then(cookies => { 243 | __uv.cookieStr = __uv.cookie.serialize(cookies, __uv.meta, true); 244 | }); 245 | }); 246 | }); 247 | const cookie = __uv.cookie.setCookie(event.data.value)[0]; 248 | 249 | if (!cookie.path) cookie.path = '/'; 250 | if (!cookie.domain) cookie.domain = __uv.meta.url.hostname; 251 | 252 | if (__uv.cookie.validateCookie(cookie, __uv.meta, true)) { 253 | if (__uv.cookieStr.length) __uv.cookieStr += '; '; 254 | __uv.cookieStr += `${cookie.name}=${cookie.value}`; 255 | }; 256 | 257 | event.respondWith(event.data.value); 258 | }); 259 | 260 | // HTML 261 | client.element.on('setInnerHTML', event => { 262 | switch (event.that.tagName) { 263 | case 'SCRIPT': 264 | event.data.value = __uv.js.rewrite(event.data.value); 265 | break; 266 | case 'STYLE': 267 | event.data.value = __uv.rewriteCSS(event.data.value); 268 | break; 269 | default: 270 | event.data.value = __uv.rewriteHtml(event.data.value); 271 | }; 272 | }); 273 | 274 | client.element.on('getInnerHTML', event => { 275 | switch (event.that.tagName) { 276 | case 'SCRIPT': 277 | event.data.value = __uv.js.source(event.data.value); 278 | break; 279 | default: 280 | event.data.value = __uv.sourceHtml(event.data.value); 281 | }; 282 | }); 283 | 284 | client.element.on('setOuterHTML', event => { 285 | event.data.value = __uv.rewriteHtml(event.data.value, { document: event.that.tagName === 'HTML' }); 286 | }); 287 | 288 | client.element.on('getOuterHTML', event => { 289 | switch (event.that.tagName) { 290 | case 'HEAD': 291 | event.data.value = __uv.sourceHtml( 292 | event.data.value.replace(/(.*)<\/head>/s, '$2') 293 | ).replace(/(.*)<\/op-head>/s, '$2'); 294 | break; 295 | case 'BODY': 296 | event.data.value = __uv.sourceHtml( 297 | event.data.value.replace(/(.*)<\/body>/s, '$2') 298 | ).replace(/(.*)<\/op-body>/s, '$2'); 299 | break; 300 | default: 301 | event.data.value = __uv.sourceHtml(event.data.value, { document: event.that.tagName === 'HTML' }); 302 | break; 303 | }; 304 | 305 | //event.data.value = __uv.sourceHtml(event.data.value, { document: event.that.tagName === 'HTML' }); 306 | }); 307 | 308 | client.document.on('write', event => { 309 | if (!event.data.html.length) return false; 310 | event.data.html = [__uv.rewriteHtml(event.data.html.join(''))]; 311 | }); 312 | 313 | client.document.on('writeln', event => { 314 | if (!event.data.html.length) return false; 315 | event.data.html = [__uv.rewriteHtml(event.data.html.join(''))]; 316 | }); 317 | 318 | client.element.on('insertAdjacentHTML', event => { 319 | event.data.html = __uv.rewriteHtml(event.data.html); 320 | }); 321 | 322 | // EventSource 323 | 324 | client.eventSource.on('construct', event => { 325 | event.data.url = __uv.rewriteUrl(event.data.url); 326 | }); 327 | 328 | 329 | client.eventSource.on('url', event => { 330 | event.data.url = __uv.rewriteUrl(event.data.url); 331 | }); 332 | 333 | // History 334 | client.history.on('replaceState', event => { 335 | if (event.data.url) event.data.url = __uv.rewriteUrl(event.data.url, '__uv' in event.that ? event.that.__uv.meta : __uv.meta); 336 | }); 337 | client.history.on('pushState', event => { 338 | if (event.data.url) event.data.url = __uv.rewriteUrl(event.data.url, '__uv' in event.that ? event.that.__uv.meta : __uv.meta); 339 | }); 340 | 341 | // Element get set attribute methods 342 | client.element.on('getAttribute', event => { 343 | if (client.element.hasAttribute.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name)) { 344 | event.respondWith( 345 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name) 346 | ); 347 | }; 348 | }); 349 | 350 | // Message 351 | client.message.on('postMessage', event => { 352 | let to = event.data.origin; 353 | let call = __uv.call; 354 | 355 | 356 | if (event.that) { 357 | call = event.that.__uv$source.call; 358 | }; 359 | 360 | event.data.origin = '*'; 361 | event.data.message = { 362 | __data: event.data.message, 363 | __origin: (event.that || event.target).__uv$source.location.origin, 364 | __to: to, 365 | }; 366 | 367 | event.respondWith( 368 | worker ? 369 | call(event.target, [event.data.message, event.data.transfer], event.that) : 370 | call(event.target, [event.data.message, event.data.origin, event.data.transfer], event.that) 371 | ); 372 | 373 | }); 374 | 375 | client.message.on('data', event => { 376 | const { value: data } = event.data; 377 | if (typeof data === 'object' && '__data' in data && '__origin' in data) { 378 | event.respondWith(data.__data); 379 | }; 380 | }); 381 | 382 | client.message.on('origin', event => { 383 | const data = client.message.messageData.get.call(event.that); 384 | if (typeof data === 'object' && data.__data && data.__origin) { 385 | event.respondWith(data.__origin); 386 | }; 387 | }); 388 | 389 | client.overrideDescriptor(window, 'origin', { 390 | get: (target, that) => { 391 | return __uv.location.origin; 392 | }, 393 | }); 394 | 395 | client.node.on('baseURI', event => { 396 | if (event.data.value.startsWith(window.location.origin)) event.data.value = __uv.sourceUrl(event.data.value); 397 | }); 398 | 399 | client.element.on('setAttribute', event => { 400 | if (event.that instanceof HTMLMediaElement && event.data.name === 'src' && event.data.value.startsWith('blob:')) { 401 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 402 | event.data.value = __uv.blobUrls.get(event.data.value); 403 | return; 404 | }; 405 | 406 | if (__uv.attrs.isUrl(event.data.name)) { 407 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 408 | event.data.value = __uv.rewriteUrl(event.data.value); 409 | }; 410 | 411 | if (__uv.attrs.isStyle(event.data.name)) { 412 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 413 | event.data.value = __uv.rewriteCSS(event.data.value, { context: 'declarationList' }); 414 | }; 415 | 416 | if (__uv.attrs.isHtml(event.data.name)) { 417 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 418 | event.data.value = __uv.rewriteHtml(event.data.value, {...__uv.meta, document: true, injectHead:__uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.configScript, __uv.cookieStr, window.location.href) }); 419 | }; 420 | 421 | if (__uv.attrs.isSrcset(event.data.name)) { 422 | event.target.call(event.that, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 423 | event.data.value = __uv.html.wrapSrcset(event.data.value); 424 | }; 425 | 426 | if (__uv.attrs.isForbidden(event.data.name)) { 427 | event.data.name = __uv.attributePrefix + '-attr-' + event.data.name; 428 | }; 429 | }); 430 | 431 | client.element.on('audio', event => { 432 | event.data.url = __uv.rewriteUrl(event.data.url); 433 | }); 434 | 435 | // Element Property Attributes 436 | client.element.hookProperty([HTMLAnchorElement, HTMLAreaElement, HTMLLinkElement, HTMLBaseElement], 'href', { 437 | get: (target, that) => { 438 | return __uv.sourceUrl( 439 | target.call(that) 440 | ); 441 | }, 442 | set: (target, that, [val]) => { 443 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-href', val) 444 | target.call(that, __uv.rewriteUrl(val)); 445 | }, 446 | }); 447 | 448 | client.element.hookProperty([HTMLScriptElement, HTMLAudioElement, HTMLVideoElement, HTMLMediaElement, HTMLImageElement, HTMLInputElement, HTMLEmbedElement, HTMLIFrameElement, HTMLTrackElement, HTMLSourceElement], 'src', { 449 | get: (target, that) => { 450 | return __uv.sourceUrl( 451 | target.call(that) 452 | ); 453 | }, 454 | set: (target, that, [val]) => { 455 | if (new String(val).toString().trim().startsWith('blob:') && that instanceof HTMLMediaElement) { 456 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-src', val) 457 | return target.call(that, __uv.blobUrls.get(val) || val); 458 | }; 459 | 460 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-src', val) 461 | target.call(that, __uv.rewriteUrl(val)); 462 | }, 463 | }); 464 | 465 | client.element.hookProperty([HTMLFormElement], 'action', { 466 | get: (target, that) => { 467 | return __uv.sourceUrl( 468 | target.call(that) 469 | ); 470 | }, 471 | set: (target, that, [val]) => { 472 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-action', val) 473 | target.call(that, __uv.rewriteUrl(val)); 474 | }, 475 | }); 476 | 477 | client.element.hookProperty([HTMLImageElement], 'srcset', { 478 | get: (target, that) => { 479 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-srcset') || target.call(that); 480 | }, 481 | set: (target, that, [val]) => { 482 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-srcset', val) 483 | target.call(that, __uv.html.wrapSrcset(val)); 484 | }, 485 | }); 486 | 487 | client.element.hookProperty(HTMLScriptElement, 'integrity', { 488 | get: (target, that) => { 489 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-integrity'); 490 | }, 491 | set: (target, that, [val]) => { 492 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-integrity', val); 493 | }, 494 | }); 495 | 496 | client.element.hookProperty(HTMLIFrameElement, 'sandbox', { 497 | get: (target, that) => { 498 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-sandbox') || target.call(that); 499 | }, 500 | set: (target, that, [val]) => { 501 | client.element.setAttribute.call(that, __uv.attributePrefix + '-attr-sandbox', val); 502 | }, 503 | }); 504 | 505 | client.element.hookProperty(HTMLIFrameElement, 'contentWindow', { 506 | get: (target, that) => { 507 | const win = target.call(that); 508 | try { 509 | if (!win.__uv) __uvHook(win, config, bare); 510 | return win; 511 | } catch (e) { 512 | return win; 513 | }; 514 | }, 515 | }); 516 | 517 | client.element.hookProperty(HTMLIFrameElement, 'contentDocument', { 518 | get: (target, that) => { 519 | const doc = target.call(that); 520 | try { 521 | const win = doc.defaultView 522 | if (!win.__uv) __uvHook(win, config, bare); 523 | return doc; 524 | } catch (e) { 525 | return win; 526 | }; 527 | }, 528 | }); 529 | 530 | client.element.hookProperty(HTMLIFrameElement, 'srcdoc', { 531 | get: (target, that) => { 532 | return client.element.getAttribute.call(that, __uv.attributePrefix + '-attr-srcdoc') || target.call(that); 533 | }, 534 | set: (target, that, [val]) => { 535 | target.call(that, __uv.rewriteHtml(val, { 536 | document: true, 537 | injectHead: __uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.configScript, __uv.cookieStr, window.location.href) 538 | })) 539 | }, 540 | }); 541 | 542 | client.node.on('getTextContent', event => { 543 | if (event.that.tagName === 'SCRIPT') { 544 | event.data.value = __uv.js.source(event.data.value); 545 | }; 546 | }); 547 | 548 | client.node.on('setTextContent', event => { 549 | if (event.that.tagName === 'SCRIPT') { 550 | event.data.value = __uv.js.rewrite(event.data.value); 551 | }; 552 | }); 553 | 554 | // Until proper rewriting is implemented for service workers. 555 | // Not sure atm how to implement it with the already built in service worker 556 | if ('serviceWorker' in window.navigator) { 557 | delete window.Navigator.prototype.serviceWorker; 558 | }; 559 | 560 | // Document 561 | client.document.on('getDomain', event => { 562 | event.data.value = __uv.domain; 563 | }); 564 | client.document.on('setDomain', event => { 565 | if (!event.data.value.toString().endsWith(__uv.meta.url.hostname.split('.').slice(-2).join('.'))) return event.respondWith(''); 566 | event.respondWith(__uv.domain = event.data.value); 567 | }) 568 | 569 | client.document.on('url', event => { 570 | event.data.value = __uv.location.href; 571 | }); 572 | 573 | client.document.on('documentURI', event => { 574 | event.data.value = __uv.location.href; 575 | }); 576 | 577 | client.document.on('referrer', event => { 578 | event.data.value = __uv.referrer || __uv.sourceUrl(event.data.value); 579 | }); 580 | 581 | client.document.on('parseFromString', event => { 582 | if (event.data.type !== 'text/html') return false; 583 | event.data.string = __uv.rewriteHtml(event.data.string, {...__uv.meta, document: true, }); 584 | }); 585 | 586 | // Attribute (node.attributes) 587 | client.attribute.on('getValue', event => { 588 | if (client.element.hasAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name)) { 589 | event.data.value = client.element.getAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name); 590 | }; 591 | }); 592 | 593 | client.attribute.on('setValue', event => { 594 | if (__uv.attrs.isUrl(event.data.name)) { 595 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 596 | event.data.value = __uv.rewriteUrl(event.data.value); 597 | }; 598 | 599 | if (__uv.attrs.isStyle(event.data.name)) { 600 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 601 | event.data.value = __uv.rewriteCSS(event.data.value, { context: 'declarationList' }); 602 | }; 603 | 604 | if (__uv.attrs.isHtml(event.data.name)) { 605 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 606 | event.data.value = __uv.rewriteHtml(event.data.value, {...__uv.meta, document: true, injectHead:__uv.createHtmlInject(__uv.handlerScript, __uv.bundleScript, __uv.configScript, __uv.cookieStr, window.location.href) }); 607 | }; 608 | 609 | if (__uv.attrs.isSrcset(event.data.name)) { 610 | client.element.setAttribute.call(event.that.ownerElement, __uv.attributePrefix + '-attr-' + event.data.name, event.data.value); 611 | event.data.value = __uv.html.wrapSrcset(event.data.value); 612 | }; 613 | 614 | }); 615 | 616 | // URL 617 | client.url.on('createObjectURL', event => { 618 | let url = event.target.call(event.that, event.data.object); 619 | if (url.startsWith('blob:' + location.origin)) { 620 | let newUrl = 'blob:' + (__uv.meta.url.href !== 'about:blank' ? __uv.meta.url.origin : window.parent.__uv.meta.url.origin) + url.slice('blob:'.length + location.origin.length); 621 | __uv.blobUrls.set(newUrl, url); 622 | event.respondWith(newUrl); 623 | } else { 624 | event.respondWith(url); 625 | }; 626 | }); 627 | 628 | client.url.on('revokeObjectURL', event => { 629 | if (__uv.blobUrls.has(event.data.url)) { 630 | const old = event.data.url; 631 | event.data.url = __uv.blobUrls.get(event.data.url); 632 | __uv.blobUrls.delete(old); 633 | }; 634 | }); 635 | 636 | client.storage.on('get', event => { 637 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name; 638 | }); 639 | 640 | client.storage.on('set', event => { 641 | if (event.that.__uv$storageObj) { 642 | event.that.__uv$storageObj[event.data.name] = event.data.value; 643 | }; 644 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name; 645 | }); 646 | 647 | client.storage.on('delete', event => { 648 | if (event.that.__uv$storageObj) { 649 | delete event.that.__uv$storageObj[event.data.name]; 650 | }; 651 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name; 652 | }); 653 | 654 | client.storage.on('getItem', event => { 655 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name; 656 | }); 657 | 658 | client.storage.on('setItem', event => { 659 | if (event.that.__uv$storageObj) { 660 | event.that.__uv$storageObj[event.data.name] = event.data.value; 661 | }; 662 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name; 663 | }); 664 | 665 | client.storage.on('removeItem', event => { 666 | if (event.that.__uv$storageObj) { 667 | delete event.that.__uv$storageObj[event.data.name]; 668 | }; 669 | event.data.name = methodPrefix + __uv.meta.url.origin + '@' + event.data.name; 670 | }); 671 | 672 | client.storage.on('clear', event => { 673 | if (event.that.__uv$storageObj) { 674 | for (const key of client.nativeMethods.keys.call(null, event.that.__uv$storageObj)) { 675 | delete event.that.__uv$storageObj[key]; 676 | client.storage.removeItem.call(event.that, methodPrefix + __uv.meta.url.origin + '@' + key); 677 | event.respondWith(); 678 | }; 679 | }; 680 | }); 681 | 682 | client.storage.on('length', event => { 683 | if (event.that.__uv$storageObj) { 684 | event.respondWith(client.nativeMethods.keys.call(null, event.that.__uv$storageObj).length); 685 | }; 686 | }); 687 | 688 | client.storage.on('key', event => { 689 | if (event.that.__uv$storageObj) { 690 | event.respondWith( 691 | (client.nativeMethods.keys.call(null, event.that.__uv$storageObj)[event.data.index] || null) 692 | ); 693 | }; 694 | }); 695 | 696 | client.websocket.on('websocket', async event => { 697 | let url; 698 | try { 699 | url = new URL(event.data.url); 700 | } catch(e) { 701 | return; 702 | }; 703 | 704 | const headers = { 705 | Host: url.host, 706 | Origin: __uv.meta.url.origin, 707 | Pragma: 'no-cache', 708 | 'Cache-Control': 'no-cache', 709 | Upgrade: 'websocket', 710 | 'User-Agent': window.navigator.userAgent, 711 | 'Connection': 'Upgrade', 712 | }; 713 | 714 | const cookies = __uv.cookie.serialize(__uv.cookies, { url }, false); 715 | 716 | if (cookies) headers.Cookie = cookies; 717 | const protocols = [...event.data.protocols]; 718 | 719 | const remote = { 720 | protocol: url.protocol, 721 | host: url.hostname, 722 | port: url.port || (url.protocol === 'wss:' ? '443' : '80'), 723 | path: url.pathname + url.search, 724 | }; 725 | 726 | if (protocols.length) headers['Sec-WebSocket-Protocol'] = protocols.join(', '); 727 | 728 | event.data.url = (__uv.bare.protocol === 'https:' ? 'wss://' : 'ws://') + __uv.bare.host + __uv.bare.pathname + 'v1/'; 729 | event.data.protocols = [ 730 | 'bare', 731 | __uv.encodeProtocol(JSON.stringify({ 732 | remote, 733 | headers, 734 | forward_headers: [ 735 | 'accept', 736 | 'accept-encoding', 737 | 'accept-language', 738 | 'sec-websocket-extensions', 739 | 'sec-websocket-key', 740 | 'sec-websocket-version', 741 | ], 742 | })), 743 | ]; 744 | 745 | const ws = new event.target(event.data.url, event.data.protocols); 746 | 747 | client.nativeMethods.defineProperty(ws, methodPrefix + 'url', { 748 | enumerable: false, 749 | value: url.href, 750 | }); 751 | 752 | event.respondWith( 753 | ws 754 | ); 755 | }); 756 | 757 | client.websocket.on('url', event => { 758 | if ('__uv$url' in event.that) { 759 | event.data.value = event.that.__uv$url; 760 | }; 761 | }); 762 | 763 | client.websocket.on('protocol', event => { 764 | if ('__uv$protocol' in event.that) { 765 | event.data.value = event.that.__uv$protocol; 766 | }; 767 | }); 768 | 769 | client.function.on('function', event => { 770 | event.data.script = __uv.rewriteJS(event.data.script); 771 | }); 772 | 773 | client.function.on('toString', event => { 774 | if (__uv.methods.string in event.that) event.respondWith(event.that[__uv.methods.string]); 775 | }); 776 | 777 | client.object.on('getOwnPropertyNames', event => { 778 | event.data.names = event.data.names.filter(element => !(__uv.filterKeys.includes(element))); 779 | }); 780 | 781 | client.object.on('getOwnPropertyDescriptors', event => { 782 | for (const forbidden of __uv.filterKeys) { 783 | delete event.data.descriptors[forbidden]; 784 | }; 785 | 786 | }); 787 | 788 | client.style.on('setProperty', event => { 789 | if (client.style.dashedUrlProps.includes(event.data.property)) { 790 | event.data.value = __uv.rewriteCSS(event.data.value, { 791 | context: 'value', 792 | ...__uv.meta 793 | }) 794 | }; 795 | }); 796 | 797 | client.style.on('getPropertyValue', event => { 798 | if (client.style.dashedUrlProps.includes(event.data.property)) { 799 | event.respondWith( 800 | __uv.sourceCSS( 801 | event.target.call(event.that, event.data.property), 802 | { 803 | context: 'value', 804 | ...__uv.meta 805 | } 806 | ) 807 | ); 808 | }; 809 | }); 810 | 811 | if ('CSS2Properties' in window) { 812 | for (const key of client.style.urlProps) { 813 | client.overrideDescriptor(window.CSS2Properties.prototype, key, { 814 | get: (target, that) => { 815 | return __uv.sourceCSS( 816 | target.call(that), 817 | { 818 | context: 'value', 819 | ...__uv.meta 820 | } 821 | ) 822 | }, 823 | set: (target, that, val) => { 824 | target.call( 825 | that, 826 | __uv.rewriteCSS(val, { 827 | context: 'value', 828 | ...__uv.meta 829 | }) 830 | ); 831 | } 832 | }); 833 | }; 834 | } else if ('HTMLElement' in window) { 835 | 836 | client.overrideDescriptor( 837 | window.HTMLElement.prototype, 838 | 'style', 839 | { 840 | get: (target, that) => { 841 | const value = target.call(that); 842 | if (!value[methodPrefix + 'modifiedStyle']) { 843 | 844 | for (const key of client.style.urlProps) { 845 | client.nativeMethods.defineProperty(value, key, { 846 | enumerable: true, 847 | configurable: true, 848 | get() { 849 | const value = client.style.getPropertyValue.call(this, key) || ''; 850 | return __uv.sourceCSS( 851 | value, 852 | { 853 | context: 'value', 854 | ...__uv.meta 855 | } 856 | ) 857 | }, 858 | set(val) { 859 | client.style.setProperty.call(this, 860 | (client.style.propToDashed[key] || key), 861 | __uv.rewriteCSS(val, { 862 | context: 'value', 863 | ...__uv.meta 864 | }) 865 | ) 866 | } 867 | }); 868 | client.nativeMethods.defineProperty(value, methodPrefix + 'modifiedStyle', { 869 | enumerable: false, 870 | value: true 871 | }); 872 | }; 873 | }; 874 | return value; 875 | } 876 | } 877 | ); 878 | }; 879 | 880 | client.style.on('setCssText', event => { 881 | event.data.value = __uv.rewriteCSS(event.data.value, { 882 | context: 'declarationList', 883 | ...__uv.meta 884 | }); 885 | }); 886 | 887 | client.style.on('getCssText', event => { 888 | event.data.value = __uv.sourceCSS(event.data.value, { 889 | context: 'declarationList', 890 | ...__uv.meta 891 | }); 892 | }); 893 | 894 | // Proper hash emulation. 895 | if (!!window.window) { 896 | __uv.addEventListener.call(window, 'hashchange', event => { 897 | if (event.__uv$dispatched) return false; 898 | event.stopImmediatePropagation(); 899 | const hash = window.location.hash; 900 | client.history.replaceState.call(window.history, '', '', event.oldURL); 901 | __uv.location.hash = hash; 902 | }); 903 | }; 904 | 905 | client.location.on('hashchange', (oldUrl, newUrl, ctx) => { 906 | if (ctx.HashChangeEvent && client.history.replaceState) { 907 | client.history.replaceState.call(window.history, '', '', __uv.rewriteUrl(newUrl)); 908 | 909 | const event = new ctx.HashChangeEvent('hashchange', { newURL: newUrl, oldURL: oldUrl }); 910 | 911 | client.nativeMethods.defineProperty(event, methodPrefix + 'dispatched', { 912 | value: true, 913 | enumerable: false, 914 | }); 915 | 916 | __uv.dispatchEvent.call(window, event); 917 | }; 918 | }); 919 | 920 | // Hooking functions & descriptors 921 | client.fetch.overrideRequest(); 922 | client.fetch.overrideUrl(); 923 | client.xhr.overrideOpen(); 924 | client.xhr.overrideResponseUrl(); 925 | client.element.overrideHtml(); 926 | client.element.overrideAttribute(); 927 | client.element.overrideInsertAdjacentHTML(); 928 | client.element.overrideAudio(); 929 | // client.element.overrideQuerySelector(); 930 | client.node.overrideBaseURI(); 931 | client.node.overrideTextContent(); 932 | client.attribute.overrideNameValue(); 933 | client.document.overrideDomain(); 934 | client.document.overrideURL(); 935 | client.document.overrideDocumentURI(); 936 | client.document.overrideWrite(); 937 | client.document.overrideReferrer(); 938 | client.document.overrideParseFromString(); 939 | client.storage.overrideMethods(); 940 | client.storage.overrideLength(); 941 | //client.document.overrideQuerySelector(); 942 | client.object.overrideGetPropertyNames(); 943 | client.object.overrideGetOwnPropertyDescriptors(); 944 | client.history.overridePushState(); 945 | client.history.overrideReplaceState(); 946 | client.eventSource.overrideConstruct(); 947 | client.eventSource.overrideUrl(); 948 | client.websocket.overrideWebSocket(); 949 | client.websocket.overrideProtocol(); 950 | client.websocket.overrideUrl(); 951 | client.url.overrideObjectURL(); 952 | client.document.overrideCookie(); 953 | client.message.overridePostMessage(); 954 | client.message.overrideMessageOrigin(); 955 | client.message.overrideMessageData(); 956 | client.workers.overrideWorker(); 957 | client.workers.overrideAddModule(); 958 | client.workers.overrideImportScripts(); 959 | client.workers.overridePostMessage(); 960 | client.style.overrideSetGetProperty(); 961 | client.style.overrideCssText(); 962 | client.navigator.overrideSendBeacon(); 963 | client.function.overrideFunction(); 964 | client.function.overrideToString(); 965 | client.location.overrideWorkerLocation( 966 | (href) => { 967 | return new URL(__uv.sourceUrl(href)); 968 | } 969 | ); 970 | 971 | client.overrideDescriptor(window, 'localStorage', { 972 | get: (target, that) => { 973 | return (that || window).__uv.lsWrap; 974 | }, 975 | }); 976 | client.overrideDescriptor(window, 'sessionStorage', { 977 | get: (target, that) => { 978 | return (that || window).__uv.ssWrap; 979 | }, 980 | }); 981 | 982 | 983 | client.override(window, 'open', (target, that, args) => { 984 | if (!args.length) return target.apply(that, args); 985 | let [url] = args; 986 | 987 | url = __uv.rewriteUrl(url); 988 | 989 | return target.call(that, url); 990 | }); 991 | 992 | __uv.$wrap = function(name) { 993 | if (name === 'location') return __uv.methods.location; 994 | if (name === 'eval') return __uv.methods.eval; 995 | return name; 996 | }; 997 | 998 | 999 | __uv.$get = function(that) { 1000 | if (that === window.location) return __uv.location; 1001 | if (that === window.eval) return __uv.eval; 1002 | if (that === window.parent) { 1003 | return window.__uv$parent; 1004 | }; 1005 | if (that === window.top) { 1006 | return window.__uv$top; 1007 | }; 1008 | return that; 1009 | }; 1010 | 1011 | __uv.eval = client.wrap(window, 'eval', (target, that, args) => { 1012 | if (!args.length || typeof args[0] !== 'string') return target.apply(that, args); 1013 | let [script] = args; 1014 | 1015 | script = __uv.rewriteJS(script); 1016 | return target.call(that, script); 1017 | }); 1018 | 1019 | __uv.call = function(target, args, that) { 1020 | return that ? target.apply(that, args) : target(...args); 1021 | }; 1022 | 1023 | __uv.call$ = function(obj, prop, args = []) { 1024 | return obj[prop].apply(obj, args); 1025 | }; 1026 | 1027 | client.nativeMethods.defineProperty(window.Object.prototype, master, { 1028 | get: () => { 1029 | return __uv; 1030 | }, 1031 | enumerable: false 1032 | }); 1033 | 1034 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.setSource, { 1035 | value: function(source) { 1036 | if (!client.nativeMethods.isExtensible(this)) return this; 1037 | 1038 | client.nativeMethods.defineProperty(this, __uv.methods.source, { 1039 | value: source, 1040 | writable: true, 1041 | enumerable: false 1042 | }); 1043 | 1044 | return this; 1045 | }, 1046 | enumerable: false, 1047 | }); 1048 | 1049 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.source, { 1050 | value: __uv, 1051 | writable: true, 1052 | enumerable: false 1053 | }); 1054 | 1055 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.location, { 1056 | configurable: true, 1057 | get() { 1058 | return (this === window.document || this === window) ? __uv.location : this.location; 1059 | }, 1060 | set(val) { 1061 | if (this === window.document || this === window) { 1062 | __uv.location.href = val; 1063 | } else { 1064 | this.location = val; 1065 | }; 1066 | }, 1067 | }); 1068 | 1069 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.parent, { 1070 | configurable: true, 1071 | get() { 1072 | const val = this.parent; 1073 | 1074 | if (this === window) { 1075 | try { 1076 | return '__uv' in val ? val : this; 1077 | } catch (e) { 1078 | return this; 1079 | }; 1080 | }; 1081 | return val; 1082 | }, 1083 | set(val) { 1084 | this.parent = val; 1085 | }, 1086 | }); 1087 | 1088 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.top, { 1089 | configurable: true, 1090 | get() { 1091 | const val = this.top; 1092 | 1093 | if (this === window) { 1094 | if (val === this.parent) return this[__uv.methods.parent]; 1095 | try { 1096 | if (!('__uv' in val)) { 1097 | let current = this; 1098 | 1099 | while (current.parent !== val) { 1100 | current = current.parent 1101 | }; 1102 | 1103 | return '__uv' in current ? current : this; 1104 | 1105 | } else { 1106 | return val; 1107 | }; 1108 | } catch (e) { 1109 | return this; 1110 | }; 1111 | }; 1112 | return val; 1113 | }, 1114 | set(val) { 1115 | this.top = val; 1116 | }, 1117 | }); 1118 | 1119 | 1120 | client.nativeMethods.defineProperty(window.Object.prototype, __uv.methods.eval, { 1121 | configurable: true, 1122 | get() { 1123 | return this === window ? __uv.eval : this.eval; 1124 | }, 1125 | set(val) { 1126 | this.eval = val; 1127 | }, 1128 | }); 1129 | }; --------------------------------------------------------------------------------