| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /******/ (function(modules) { // webpackBootstrap
- /******/ // The module cache
- /******/ var installedModules = {};
- /******/
- /******/ // The require function
- /******/ function __webpack_require__(moduleId) {
- /******/
- /******/ // Check if module is in cache
- /******/ if(installedModules[moduleId]) {
- /******/ return installedModules[moduleId].exports;
- /******/ }
- /******/ // Create a new module (and put it into the cache)
- /******/ var module = installedModules[moduleId] = {
- /******/ i: moduleId,
- /******/ l: false,
- /******/ exports: {}
- /******/ };
- /******/
- /******/ // Execute the module function
- /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
- /******/
- /******/ // Flag the module as loaded
- /******/ module.l = true;
- /******/
- /******/ // Return the exports of the module
- /******/ return module.exports;
- /******/ }
- /******/
- /******/
- /******/ // expose the modules object (__webpack_modules__)
- /******/ __webpack_require__.m = modules;
- /******/
- /******/ // expose the module cache
- /******/ __webpack_require__.c = installedModules;
- /******/
- /******/ // define getter function for harmony exports
- /******/ __webpack_require__.d = function(exports, name, getter) {
- /******/ if(!__webpack_require__.o(exports, name)) {
- /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
- /******/ }
- /******/ };
- /******/
- /******/ // define __esModule on exports
- /******/ __webpack_require__.r = function(exports) {
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
- /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
- /******/ }
- /******/ Object.defineProperty(exports, '__esModule', { value: true });
- /******/ };
- /******/
- /******/ // create a fake namespace object
- /******/ // mode & 1: value is a module id, require it
- /******/ // mode & 2: merge all properties of value into the ns
- /******/ // mode & 4: return value when already ns object
- /******/ // mode & 8|1: behave like require
- /******/ __webpack_require__.t = function(value, mode) {
- /******/ if(mode & 1) value = __webpack_require__(value);
- /******/ if(mode & 8) return value;
- /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
- /******/ var ns = Object.create(null);
- /******/ __webpack_require__.r(ns);
- /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
- /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
- /******/ return ns;
- /******/ };
- /******/
- /******/ // getDefaultExport function for compatibility with non-harmony modules
- /******/ __webpack_require__.n = function(module) {
- /******/ var getter = module && module.__esModule ?
- /******/ function getDefault() { return module['default']; } :
- /******/ function getModuleExports() { return module; };
- /******/ __webpack_require__.d(getter, 'a', getter);
- /******/ return getter;
- /******/ };
- /******/
- /******/ // Object.prototype.hasOwnProperty.call
- /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
- /******/
- /******/ // __webpack_public_path__
- /******/ __webpack_require__.p = "";
- /******/
- /******/
- /******/ // Load entry module and return exports
- /******/ return __webpack_require__(__webpack_require__.s = "./src/index.js");
- /******/ })
- /************************************************************************/
- /******/ ({
- /***/ "./src/fakeloc.js":
- /*!************************!*\
- !*** ./src/fakeloc.js ***!
- \************************/
- /*! exports provided: init */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"init\", function() { return init; });\n/* harmony import */ var _urlx__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./urlx */ \"./src/urlx.js\");\n\n\n\n/**\n * @param {string} url \n */\nfunction decOrigin(url) {\n const u = new URL(url)\n _urlx__WEBPACK_IMPORTED_MODULE_0__[\"decUrlObj\"](u)\n return u.origin\n}\n\nfunction setup(obj, fakeLoc) {\n Reflect.defineProperty(obj, '__location', {\n get() {\n return fakeLoc\n },\n set(val) {\n console.log('[jsproxy] %s set location: %s', obj, val)\n fakeLoc.href = val\n }\n })\n}\n\n/**\n * @param {Window} win \n * @param {Hook} hook \n */\nfunction init(win, hook) {\n let loc = win.location\n\n // TODO: iframe 场合下存在问题\n // 比如 youtube 首页缺少这个判断会报错\n if (loc.href === 'about:blank') {\n loc = win.top.location\n }\n\n const fakeLoc = Object.setPrototypeOf({\n get href() {\n // console.log('[jsproxy] get location.href')\n return _urlx__WEBPACK_IMPORTED_MODULE_0__[\"decUrlStr\"](loc.href)\n },\n\n get protocol() {\n // TODO: 未考虑非 https 的页面 URL\n return loc.protocol\n },\n\n get host() {\n // TODO: 未考虑带端口的页面 URL\n // console.log('[jsproxy] get location.host')\n return _urlx__WEBPACK_IMPORTED_MODULE_0__[\"decHost\"](loc.host)\n },\n\n get hostname() {\n // console.log('[jsproxy] get location.hostname')\n return _urlx__WEBPACK_IMPORTED_MODULE_0__[\"decHost\"](loc.hostname)\n },\n\n get port() {\n // TODO: 未考虑带端口的页面 URL\n return loc.port\n },\n\n get pathname() {\n return loc.pathname\n },\n\n get search() {\n return loc.search\n },\n\n get hash() {\n return loc.hash\n },\n\n get origin() {\n // console.log('[jsproxy] get location.origin')\n return decOrigin(loc.origin)\n },\n\n get ancestorOrigins() {\n // TODO: DOMStringList[]\n // console.log('[jsproxy] get location.ancestorOrigins')\n return [...loc.ancestorOrigins].map(decOrigin)\n },\n\n set href(val) {\n console.log('[jsproxy] set location.href:', val)\n loc.href = _urlx__WEBPACK_IMPORTED_MODULE_0__[\"encUrlStr\"](val, loc)\n },\n\n set protocol(val) {\n const u = new URL(loc)\n // TODO: \n },\n\n set host(val) {\n console.log('[jsproxy] set location.host:', val)\n // TODO:\n },\n\n set hostname(val) {\n console.log('[jsproxy] set location.hostname:', val)\n loc.hostname = _urlx__WEBPACK_IMPORTED_MODULE_0__[\"encHost\"](val)\n },\n\n set port(val) {\n console.log('[jsproxy] set location.port:', val)\n // TODO:\n },\n\n set pathname(val) {\n loc.pathname = val\n },\n\n set search(val) {\n loc.search = val\n },\n\n set hash(val) {\n loc.hash = val\n },\n\n reload(...args) {\n loc.reload(...args)\n },\n\n replace(val) {\n if (val) {\n console.log('[jsproxy] location.replace:', val)\n arguments[0] = _urlx__WEBPACK_IMPORTED_MODULE_0__[\"encUrlStr\"](val, loc)\n }\n loc.replace(...arguments)\n },\n\n assign(val) {\n if (val) {\n console.log('[jsproxy] location.assign:', val)\n arguments[0] = _urlx__WEBPACK_IMPORTED_MODULE_0__[\"encUrlStr\"](val, loc)\n }\n loc.assign(...arguments)\n },\n\n toString() {\n const val = loc.toString(...arguments)\n return _urlx__WEBPACK_IMPORTED_MODULE_0__[\"decUrlStr\"](val)\n },\n\n toLocaleString() {\n const val = loc.toLocaleString(...arguments)\n return _urlx__WEBPACK_IMPORTED_MODULE_0__[\"decUrlStr\"](val)\n },\n }, loc.constructor.prototype)\n\n\n setup(win, fakeLoc)\n setup(win.document, fakeLoc)\n}\n\n\n//# sourceURL=webpack:///./src/fakeloc.js?");
- /***/ }),
- /***/ "./src/hook.js":
- /*!*********************!*\
- !*** ./src/hook.js ***!
- \*********************/
- /*! exports provided: DELETE, RETURN, createHook */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"DELETE\", function() { return DELETE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RETURN\", function() { return RETURN; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createHook\", function() { return createHook; });\nconst DELETE = {}\nconst RETURN = {}\n\n/**\n * @param {Window} win \n */\nfunction createHook(win) {\n // const {\n // Reflect,\n // WeakMap,\n // } = win\n\n const {\n apply,\n getOwnPropertyDescriptor,\n defineProperty,\n } = Reflect\n\n const rawMap = new WeakMap()\n\n\n /**\n * hook function\n * \n * @param {object} obj \n * @param {string} key \n * @param {Function} factory \n */\n function func(obj, key, factory) {\n const oldFn = obj[key]\n if (!oldFn) {\n return false\n }\n const newFn = factory(oldFn)\n for (const k in oldFn) {\n newFn[k] = oldFn[k]\n }\n newFn.prototype = oldFn.prototype\n rawMap.set(newFn, oldFn)\n obj[key] = newFn\n return true\n }\n\n /**\n * hook property\n * \n * @param {object} obj \n * @param {string} key \n * @param {Function} g \n * @param {Function} s \n */\n function prop(obj, key, g, s) {\n const desc = getOwnPropertyDescriptor(obj, key)\n if (!desc) {\n return false\n }\n if (g) {\n func(desc, 'get', g)\n }\n if (s) {\n func(desc, 'set', s)\n }\n defineProperty(obj, key, desc)\n return true\n }\n\n\n function hookElemProp(proto, name, onget, onset) {\n prop(proto, name,\n getter => function() {\n const val = getter.call(this)\n return onget.call(this, val)\n },\n setter => function(val) {\n const ret = onset.call(this, val)\n if (ret !== null) {\n val = ret\n }\n setter.call(this, val)\n }\n )\n }\n\n const toLCase = ''.toLocaleLowerCase\n const elemProto = win.Element.prototype\n const rawGetAttr = elemProto.getAttribute\n const rawSetAttr = elemProto.setAttribute\n\n const tagAttrHandlersMap = {}\n const tagTextHandlerMap = {}\n const tagKeySetMap = {}\n const tagKeyGetMap = {}\n\n\n function attr(tag, proto, ...handlers) {\n let hasBind, hasAttr\n let keySetMap, keyGetMap\n\n handlers.forEach(v => {\n // 划线转驼峰('http-equiv' -> 'httpEquiv')\n const name = v.name.replace(/-(\\w)/g, (_, $1) => {\n return $1.toUpperCase()\n })\n hookElemProp(proto, name, v.onget, v.onset)\n\n // #text\n if (name === 'innerText') {\n tagTextHandlerMap[tag] = v\n return\n }\n\n // attribute\n if (tagAttrHandlersMap[tag]) {\n tagAttrHandlersMap[tag].push(v)\n hasBind = true\n } else {\n tagAttrHandlersMap[tag] = [v]\n tagKeySetMap[tag] = {}\n tagKeyGetMap[tag] = {}\n }\n\n if (!keySetMap) {\n keySetMap = tagKeySetMap[tag]\n keyGetMap = tagKeyGetMap[tag]\n }\n const key = toLCase.call(name)\n keySetMap[key] = v.onset\n keyGetMap[key] = v.onget\n hasAttr = true\n })\n\n if (hasBind || !hasAttr) {\n return\n }\n\n // 如果之前调用过 setAttribute,那么直接返回上次设置的值。\n // 如果没有调用过则回调 onget\n func(proto, 'getAttribute', oldFn => function(name) {\n const key = toLCase.call(name)\n const get = keyGetMap[key]\n if (get) {\n const lastVal = this['_k' + key]\n if (lastVal !== undefined) {\n return lastVal\n }\n const val = apply(oldFn, this, arguments)\n return get(val)\n }\n return apply(oldFn, this, arguments)\n })\n\n func(proto, 'setAttribute', oldFn => function(name, val) {\n const key = toLCase.call(name)\n const set = keySetMap[key]\n if (set) {\n this['_k' + key] = val\n\n const ret = set.call(this, val)\n if (ret === DELETE) {\n this.removeAttribute(key)\n return\n }\n if (ret === RETURN) {\n return\n }\n arguments[1] = ret\n }\n return apply(oldFn, this, arguments)\n })\n\n // TODO: setAttributeNode\n // ...\n }\n\n /**\n * @param {Text} node\n * @param {object} handler\n * @param {Element} elem \n */\n function parseNewTextNode(node, handler, elem) {\n const val = node.nodeValue\n const ret = handler.onset.call(elem, val)\n if (ret === DELETE) {\n node.remove()\n return\n }\n if (ret === RETURN) {\n return\n }\n node.nodeValue = ret\n }\n\n /**\n * @param {Element} elem \n * @param {object} handler\n */\n function parseNewElemNode(elem, handler) {\n const name = handler.name\n if (!elem.hasAttribute(name)) {\n return\n }\n const val = rawGetAttr.call(elem, name)\n const ret = handler.onset.call(elem, val)\n if (ret === DELETE) {\n elem.removeAttribute(name)\n return\n }\n if (ret === RETURN) {\n return\n }\n rawSetAttr.call(elem, name, ret)\n }\n\n /**\n * @param {MutationRecord[]} mutations \n */\n function parseMutations(mutations) {\n mutations.forEach(mutation => {\n mutation.addedNodes.forEach(node => {\n switch (node.nodeType) {\n case 1: // ELEMENT_NODE\n const handlers = tagAttrHandlersMap[node.tagName]\n handlers && handlers.forEach(v => {\n parseNewElemNode(node, v)\n })\n break\n case 3: // TEXT_NODE\n const elem = node.parentElement\n if (elem) {\n const handler = tagTextHandlerMap[elem.tagName]\n if (handler) {\n parseNewTextNode(node, handler, elem)\n }\n }\n break\n }\n })\n })\n }\n\n\n const observer = new win.MutationObserver(parseMutations)\n observer.observe(win.document, {\n childList: true,\n subtree: true,\n })\n\n // win.addEventListener('DOMContentLoaded', e => {\n // parseMutations(observer.takeRecords())\n // observer.disconnect()\n // })\n\n // hide source code\n func(Function.prototype, 'toString', oldFn => function() {\n return apply(oldFn, rawMap.get(this) || this, arguments)\n })\n \n return {\n func,\n prop,\n attr,\n }\n}\n\n//# sourceURL=webpack:///./src/hook.js?");
- /***/ }),
- /***/ "./src/hostlist.js":
- /*!*************************!*\
- !*** ./src/hostlist.js ***!
- \*************************/
- /*! exports provided: MY_ROOT, HOST_LIST */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MY_ROOT\", function() { return MY_ROOT; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"HOST_LIST\", function() { return HOST_LIST; });\n// THIS FILE WAS GENERATED BY build.sh\n// DO NOT MODIFY\nconst MY_ROOT = 'jsproxy.tk'\nconst HOST_LIST = [\n ['gg', 'google.com'],\n ['gc', 'google.cn'],\n ['gk', 'google.com.hk'],\n ['gu', 'googleusercontent.com'],\n ['gs', 'googlesource.com'],\n ['wk', 'wikipedia.org'],\n ['m.wk', 'm.wikipedia.org'],\n ['so', 'stackoverflow.com'],\n ['se', 'stackexchange.com'],\n ['sf', 'serverfault.com'],\n ['su', 'superuser.com'],\n ['au', 'askubuntu.com'],\n ['gh', 'github.com'],\n ['qr', 'quora.com'],\n ['ux', 'unix.com'],\n ['mz', 'mozilla.org'],\n ['w3', 'w3schools.com'],\n ['cr', 'chromium.org'],\n ['my', 'myspace.com'],\n ['fb', 'facebook.com'],\n ['yt', 'youtube.com'],\n ['tw', 'twitter.com'],\n ['fl', 'flickr.com'],\n ['rd', 'reddit.com'],\n ['bg', 'blogger.com'],\n ['wp', 'wordpress.com'],\n ['md', 'medium.com'],\n ['hn', 'hackernoon.com'],\n ['yh', 'yahoo.com'],\n ['bc', 'bbc.com'],\n ['th', 'twitch.tv'],\n ['sc', 'steamcommunity.com'],\n]\n\n\n//# sourceURL=webpack:///./src/hostlist.js?");
- /***/ }),
- /***/ "./src/index.js":
- /*!**********************!*\
- !*** ./src/index.js ***!
- \**********************/
- /*! no static exports found */
- /***/ (function(module, exports, __webpack_require__) {
- eval("function main() {\n if ('onclick' in self) {\n // page env\n return __webpack_require__(/*! ./page.js */ \"./src/page.js\")\n }\n if ('onfetch' in self) {\n // sw env\n return __webpack_require__(/*! ./sw.js */ \"./src/sw.js\")\n }\n return __webpack_require__(/*! ./worker.js */ \"./src/worker.js\")\n}\n\nmain()\n\n//# sourceURL=webpack:///./src/index.js?");
- /***/ }),
- /***/ "./src/inject.js":
- /*!***********************!*\
- !*** ./src/inject.js ***!
- \***********************/
- /*! exports provided: htmlRemote, htmlLocal, jsRemote, workerRemote, workerLocal */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"htmlRemote\", function() { return htmlRemote; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"htmlLocal\", function() { return htmlLocal; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"jsRemote\", function() { return jsRemote; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"workerRemote\", function() { return workerRemote; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"workerLocal\", function() { return workerLocal; });\n/* harmony import */ var _urlx__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./urlx */ \"./src/urlx.js\");\n/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util.js */ \"./src/util.js\");\n/* harmony import */ var _jsfilter_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./jsfilter.js */ \"./src/jsfilter.js\");\n\n\n\n\n\nconst RES_HOST = _urlx__WEBPACK_IMPORTED_MODULE_0__[\"getMyRootHost\"]()\nconst HELPER_URL = `//${RES_HOST}/x.js`\n\n// 为了简化注入位置的分析,这里直接插到 HTML 开头\n// 所以页面里会出现两个 <!DOCTYPE>\nconst HTML_BEG = _util_js__WEBPACK_IMPORTED_MODULE_1__[\"strToBytes\"](\n `<!DOCTYPE html><script src=\"${HELPER_URL}\"></script>`\n)\n\n// Worker \nconst WORKER_BEG = _util_js__WEBPACK_IMPORTED_MODULE_1__[\"strToBytes\"](\n `importScripts('${HELPER_URL}');`\n)\n\n\n/**\n * @param {Response} res\n * @param {Object} resOpt\n */\nfunction htmlRemote(res, resOpt) {\n const reader = res.body.getReader()\n let injected\n\n const stream = new ReadableStream({\n async pull(controller) {\n if (!injected) {\n injected = true\n controller.enqueue(HTML_BEG)\n }\n const r = await reader.read()\n if (r.done) {\n controller.close()\n return\n }\n controller.enqueue(r.value)\n }\n })\n return new Response(stream, resOpt)\n}\n\n\n// 处理 data、blob 协议的页面\nfunction htmlLocal(uri) {\n // TODO:\n}\n\n\n/**\n * @param {Response} res\n * @param {Object} resOpt\n */\nasync function jsRemote(res, resOpt, charset) {\n // 之后会分析语法树,所以不使用流模式\n const buf = await res.arrayBuffer()\n const ret = await _jsfilter_js__WEBPACK_IMPORTED_MODULE_2__[\"parseBin\"](buf, charset)\n if (ret) {\n resOpt.headers = new Headers(resOpt.headers)\n resOpt.headers.set('content-type', 'text/javascript')\n }\n return new Response(ret || buf, resOpt)\n}\n\n\nfunction workerRemote(res, resOpt, charset) {\n // TODO: \n}\n\n\n// 处理 data、blob 协议的 Worker\nfunction workerLocal(data) {\n // TODO: \n}\n\n//# sourceURL=webpack:///./src/inject.js?");
- /***/ }),
- /***/ "./src/jsfilter.js":
- /*!*************************!*\
- !*** ./src/jsfilter.js ***!
- \*************************/
- /*! exports provided: parseSync, parseBin */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSync\", function() { return parseSync; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseBin\", function() { return parseBin; });\n/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util.js */ \"./src/util.js\");\n\n\n\n/**\n * @param {string} code \n */\nfunction parseSync(code) {\n // TODO: parse js ast\n let match\n code = code.replace(/(\\b)location(\\b)/g, (s, $1, $2) => {\n match = true\n return $1 + '__location' + $2\n })\n if (match) {\n return code\n }\n}\n\n/**\n * @param {Uint8Array} buf\n */\nasync function parseBin(buf, charset) {\n const str = _util_js__WEBPACK_IMPORTED_MODULE_0__[\"bytesToStr\"](buf, charset)\n const ret = parseSync(str)\n if (ret) {\n return _util_js__WEBPACK_IMPORTED_MODULE_0__[\"strToBytes\"](ret)\n }\n if (!_util_js__WEBPACK_IMPORTED_MODULE_0__[\"isUtf8\"](charset)) {\n return _util_js__WEBPACK_IMPORTED_MODULE_0__[\"strToBytes\"](str)\n }\n}\n\n//# sourceURL=webpack:///./src/jsfilter.js?");
- /***/ }),
- /***/ "./src/nav.js":
- /*!********************!*\
- !*** ./src/nav.js ***!
- \********************/
- /*! exports provided: init */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"init\", function() { return init; });\n/* harmony import */ var _urlx_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./urlx.js */ \"./src/urlx.js\");\n\n\n/**\n * page navigate intercept\n * \n * @param {Window} win \n * @param {Hook} hook \n */\nfunction init(win, hook) {\n const {\n location,\n Reflect,\n } = win\n\n const {\n apply,\n } = Reflect\n\n const linkProto = win.HTMLAnchorElement.prototype\n const areaProto = win.HTMLAreaElement.prototype\n const formProto = win.HTMLFormElement.prototype\n\n function hookNavAttr(tag, proto, name) {\n hook.attr(tag, proto, {\n name,\n onget(val) {\n const u = new URL(val, location)\n _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"unpack\"](u)\n return u.href\n },\n onset(val) {\n const u = new URL(val, location)\n _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"pack\"](u, false, false)\n return u.href\n }\n })\n }\n hookNavAttr('A', linkProto, 'href')\n hookNavAttr('AREA', areaProto, 'href')\n hookNavAttr('FORM', formProto, 'action')\n\n\n // TODO:\n function hookLinkProp(proto) {\n hook.prop(proto, 'hostname',\n getter => function() {\n const val = getter.call(this)\n return val\n },\n setter => function(val) {\n console.log('[jsproxy] set link hostname:', val)\n setter.call(this, val)\n }\n )\n\n hook.prop(proto, 'host',\n getter => function() {\n const val = getter.call(this)\n return val\n },\n setter => function(val) {\n console.log('[jsproxy] set link host:', val)\n setter.call(this, val)\n }\n )\n\n hook.prop(proto, 'protocol',\n getter => function() {\n const val = getter.call(this)\n return val\n },\n setter => function(val) {\n console.log('[jsproxy] set link protocol:', val)\n setter.call(this, val)\n }\n )\n\n hook.prop(proto, 'port',\n getter => function() {\n const val = getter.call(this)\n return val\n },\n setter => function(val) {\n console.log('[jsproxy] set link port:', val)\n setter.call(this, val)\n }\n )\n\n hook.prop(proto, 'search',\n getter => function() {\n const val = getter.call(this)\n return val\n },\n setter => function(val) {\n console.log('[jsproxy] set link search:', val)\n setter.call(this, val)\n }\n )\n }\n hookLinkProp(linkProto)\n hookLinkProp(areaProto)\n\n\n // hook window.open()\n hook.func(win, 'open', oldFn => function(url) {\n if (url) {\n const u = new URL(url, location)\n _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"pack\"](u, false, false)\n arguments[0] = u.href\n }\n return apply(oldFn, this, arguments)\n })\n\n\n //\n // hook <base>\n //\n const baseProto = win.HTMLBaseElement.prototype\n\n hook.attr('BASE', baseProto, {\n name: 'href',\n onget(val) {\n return _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"decUrlStr\"](val)\n },\n onset(val) {\n // console.log('[jsproxy] set base.href:', val)\n // val = getFinalUrl(val)\n return _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"encUrlStr\"](val, location)\n }\n })\n\n //\n // hook <meta>\n //\n const metaProto = win.HTMLMetaElement.prototype\n\n hook.attr('META', metaProto, {\n name: 'http-equiv',\n onget(val) {\n // TODO: \n return val\n },\n onset(val) {\n return val\n }\n })\n}\n\n//# sourceURL=webpack:///./src/nav.js?");
- /***/ }),
- /***/ "./src/page.js":
- /*!*********************!*\
- !*** ./src/page.js ***!
- \*********************/
- /*! no exports provided */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _hook_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hook.js */ \"./src/hook.js\");\n/* harmony import */ var _urlx_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./urlx.js */ \"./src/urlx.js\");\n/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util.js */ \"./src/util.js\");\n/* harmony import */ var _nav_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./nav.js */ \"./src/nav.js\");\n/* harmony import */ var _jsfilter_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./jsfilter.js */ \"./src/jsfilter.js\");\n/* harmony import */ var _fakeloc_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./fakeloc.js */ \"./src/fakeloc.js\");\n\n\n\n\n\n\n\n\n/**\n * @param {Window} win \n */\nfunction initWin(win) {\n if (!win) {\n return\n }\n try {\n if (win.Math.__flag) {\n return // setuped\n }\n win.Math.__flag = 1\n } catch (err) {\n return // not same origin\n }\n\n const {\n // WeakSet,\n // Reflect,\n // RegExp,\n // URL,\n // Proxy,\n document,\n location,\n navigator,\n } = win\n\n const {\n apply,\n construct,\n } = Reflect\n\n const isExtPageMode = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"isMyExtHost\"](location.hostname)\n\n // hook Function\n // hook.func(window, 'Function', oldFn => function() {\n // return apply(oldFn, this, arguments)\n // })\n\n const hook = Object(_hook_js__WEBPACK_IMPORTED_MODULE_0__[\"createHook\"])(win)\n _nav_js__WEBPACK_IMPORTED_MODULE_3__[\"init\"](win, hook)\n\n // hook window/document.location\n _fakeloc_js__WEBPACK_IMPORTED_MODULE_5__[\"init\"](win, hook)\n\n\n // hook document.domain\n const docProto = win.Document.prototype\n\n hook.prop(docProto, 'domain',\n getter => function() {\n const val = getter.call(this)\n return _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"decHost\"](val)\n },\n setter => function(val) {\n if (isExtPageMode) {\n console.warn('[jsproxy] unsafe domain')\n val = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"getMyExtHost\"]()\n } else {\n val = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encHost\"](val)\n }\n setter.call(this, val)\n }\n )\n\n // hook document.cookie\n const R_COOKIE_DOMAIN = /(?<=;\\s*domain=)[^;]+/i\n\n hook.prop(docProto, 'cookie', null,\n setter => function(val) {\n val = val.replace(R_COOKIE_DOMAIN, rHost => {\n if (isExtPageMode) {\n return ''\n }\n if (rHost[0] === '.') {\n rHost = rHost.substr(1)\n }\n const vHost = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encHost\"](rHost)\n if (_urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"isMyRootHost\"](vHost)) {\n console.warn('[jsproxy] invalid cookie domain:', rHost, vHost)\n }\n return vHost\n })\n setter.call(this, val)\n }\n )\n\n // uri api\n function getUriHook(getter) {\n return function() {\n const val = getter.call(this)\n return _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"decUrlStr\"](val)\n }\n }\n hook.prop(docProto, 'referrer', getUriHook)\n hook.prop(docProto, 'URL', getUriHook)\n hook.prop(docProto, 'documentURI', getUriHook)\n hook.prop(win.Node.prototype, 'baseURI', getUriHook)\n\n\n // disable ServiceWorker\n const swProto = win.ServiceWorkerContainer.prototype\n if (swProto) {\n hook.func(swProto, 'register', oldFn => function() {\n console.warn('access serviceWorker.register blocked')\n return new Promise(function() {})\n })\n hook.func(swProto, 'getRegistration', oldFn => function() {\n console.warn('access serviceWorker.getRegistration blocked')\n return new Promise(function() {})\n })\n hook.func(swProto, 'getRegistrations', oldFn => function() {\n console.warn('access serviceWorker.getRegistrations blocked')\n return new Promise(function() {})\n })\n }\n\n //\n // hook history\n //\n function historyStateHook(oldFn) {\n return function(_0, _1, url) {\n if (url) {\n arguments[2] = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](url, location)\n }\n // console.log('[jsproxy] history.replaceState', url)\n return apply(oldFn, this, arguments)\n }\n }\n const historyProto = win.History.prototype\n hook.func(historyProto, 'pushState', historyStateHook)\n hook.func(historyProto, 'replaceState', historyStateHook)\n\n\n //\n hook.func(navigator, 'registerProtocolHandler', oldFn => function(_0, url, _1) {\n console.log('registerProtocolHandler:', arguments)\n return apply(oldFn, this, arguments)\n })\n \n\n // hook Performance API\n hook.prop(win.PerformanceEntry.prototype, 'name', getUriHook)\n\n //\n // hook iframe\n //\n const iframeProto = win.HTMLIFrameElement.prototype\n hook.prop(iframeProto, 'contentWindow',\n getter => function() {\n const win = getter.call(this)\n initWin(win)\n return win\n }\n )\n\n hook.prop(iframeProto, 'contentDocument',\n getter => function() {\n const doc = getter.call(this)\n if (doc) {\n initWin(doc.defaultView)\n }\n return doc\n }\n )\n\n \n hook.attr('IFRAME', iframeProto, {\n name: 'src',\n onget(val) {\n return _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"decUrlStr\"](val)\n },\n onset(val) {\n val = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](val, location)\n console.log('[jsproxy] set <iframe> src', val)\n return val\n }\n })\n\n\n const embedProto = win.HTMLEmbedElement.prototype\n hook.attr('EMBED', embedProto, {\n name: 'src',\n onget(val) {\n console.log('[jsproxy] get <embed> src:', val)\n return val\n },\n onset(val) {\n console.log('[jsproxy] set <embed> src:', val)\n return val\n }\n })\n\n\n const objectProto = win.HTMLObjectElement.prototype\n hook.attr('OBJECT', objectProto, {\n name: 'data',\n onget(val) {\n console.log('[jsproxy] get <object> src:', val)\n return val\n },\n onset(val) {\n console.log('[jsproxy] set <object> src:', val)\n return val\n }\n })\n\n\n const frames = win.frames\n\n win.frames = new Proxy(frames, {\n get(_, key) {\n if (typeof key === 'number') {\n console.log('get frames index:', key)\n const win = frames[key]\n initWin(win)\n return win\n } else {\n return frames[key]\n }\n }\n })\n\n //\n // hook message origin\n //\n hook.func(win, 'postMessage', oldFn => function(msg, origin) {\n // origin 必须是完整的 URL(不接受 // 开头的相对协议)\n if (origin && origin !== '*') {\n arguments[1] = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](origin)\n }\n return apply(oldFn, this, arguments)\n })\n\n hook.prop(win.MessageEvent.prototype, 'origin', getUriHook)\n\n //\n // hook xhr\n //\n const xhrProto = win.XMLHttpRequest.prototype\n hook.func(xhrProto, 'open', oldFn => function(_0, url, async) {\n if (url) {\n arguments[1] = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](url, location)\n }\n if (async === false) {\n console.log('[jsproxy] sync xhr is disabled')\n arguments[2] = true\n }\n return apply(oldFn, this, arguments)\n })\n\n\n hook.func(win, 'fetch', oldFn => function(v) {\n if (v && v.url) {\n // v is Request\n url = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](url)\n arguments[0] = new Request(url, v)\n } else {\n // v is string\n arguments[0] = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](v, location)\n }\n return apply(oldFn, this, arguments)\n })\n\n\n // hook Worker\n function workHook(oldFn) {\n return function(url) {\n if (url) {\n console.log('[jsproxy] new worker:', url)\n arguments[0] = _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"encUrlStr\"](url, location)\n }\n return construct(oldFn, arguments)\n }\n }\n hook.func(win, 'Worker', workHook)\n hook.func(win, 'SharedWorker', workHook)\n\n\n // hook WebSocket\n hook.func(win, 'WebSocket', oldFn => function(url) {\n if (url) {\n const u = new URL(url)\n _urlx_js__WEBPACK_IMPORTED_MODULE_1__[\"pack\"](u, true, true)\n arguments[0] = u.href\n }\n return construct(oldFn, arguments)\n })\n\n\n const scriptProto = win.HTMLScriptElement.prototype\n\n hook.attr('SCRIPT', scriptProto,\n // 强制使用 utf-8 编码,方便 SW 编码\n {\n name: 'charset',\n onget(val) {\n return this._charset || val\n },\n onset(val) {\n if (!_util_js__WEBPACK_IMPORTED_MODULE_2__[\"isUtf8\"](val)) {\n val = 'utf-8'\n }\n this._charset = val\n return val\n }\n },\n // 禁止设置内容校验\n {\n name: 'integrity',\n onget(val) {\n return this._integrity\n },\n onset(val) {\n this._integrity = val\n return _hook_js__WEBPACK_IMPORTED_MODULE_0__[\"DELETE\"]\n }\n },\n // // \n // {\n // name: 'type',\n // onget(val) {\n // return val\n // },\n // onset(val) {\n // updateScript(this)\n // return val\n // }\n // },\n // \n {\n name: 'innerText',\n onget(val) {\n return val\n },\n onset(val) {\n updateScript(this)\n return val\n }\n })\n\n // text 属性只有 prop 没有 attr\n let scriptTextSetter\n\n function scriptGetJs(getter) {\n return function() {\n return getter.call(this)\n }\n }\n function scriptSetJs(setter) {\n scriptTextSetter = setter\n\n return function(val) {\n updateScript(this)\n setter.call(this, val)\n }\n }\n hook.prop(scriptProto, 'innerHTML', scriptGetJs, scriptSetJs)\n hook.prop(scriptProto, 'text', scriptGetJs, scriptSetJs)\n\n const JS_MIME = {\n '': true,\n 'text/javascript': true,\n 'application/javascript': true,\n 'module': true,\n }\n \n /**\n * @param {HTMLScriptElement} elem \n */\n function updateScript(elem) {\n const type = elem.type\n if (!JS_MIME[type]) {\n return\n }\n const code = elem.text\n if (!code) {\n return\n }\n if (elem.__parsed) {\n return\n }\n const ret = _jsfilter_js__WEBPACK_IMPORTED_MODULE_4__[\"parseSync\"](code)\n if (ret) {\n scriptTextSetter.call(elem, ret)\n }\n elem.__parsed = true\n }\n}\n\ninitWin(self)\n\nif (self !== parent) {\n parent.postMessage('__READY', '*')\n}\n\ndocument.currentScript.remove()\nconsole.log('[jsproxy] helper inited', location.href)\n\n\n//# sourceURL=webpack:///./src/page.js?");
- /***/ }),
- /***/ "./src/sw.js":
- /*!*******************!*\
- !*** ./src/sw.js ***!
- \*******************/
- /*! no exports provided */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _urlx_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./urlx.js */ \"./src/urlx.js\");\n/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util.js */ \"./src/util.js\");\n/* harmony import */ var _inject_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./inject.js */ \"./src/inject.js\");\n\n\n\n\nconst TYPE_HTML = 1\nconst TYPE_JS = 2\nconst TYPE_WORKER = 2\n\n\n/**\n * \n * @param {Request} req \n * @param {URL} urlObj \n */\nasync function forward(req, urlObj, redirNum = 0) {\n const hasCors = (req.mode === 'cors')\n _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"pack\"](urlObj, true, hasCors)\n\n let reqType = 0\n if (req.mode === 'navigate') {\n reqType = TYPE_HTML\n } else {\n const dest = req.destination\n if (dest === 'script') {\n reqType = TYPE_JS\n } else if (dest === 'worker') {\n reqType = TYPE_WORKER\n }\n }\n\n const reqOpt = {\n // mode: reqType ? 'cors' : req.mode,\n mode: 'cors',\n method: req.method,\n headers: req.headers,\n credentials: req.credentials,\n signal: req.signal,\n // referrerPolicy: 'no-referrer',\n referrer: req.referrer,\n }\n\n if (req.method === 'POST') {\n // TODO: 解决 stream is lock 的错误\n const buf = await req.arrayBuffer()\n if (buf.byteLength > 0) {\n reqOpt.body = buf\n }\n }\n\n const res = await fetch(urlObj, reqOpt)\n const resStatus = res.status\n\n\n // https://fetch.spec.whatwg.org/#statuses\n const isEmpty =\n (resStatus === 101) ||\n (resStatus === 204) ||\n (resStatus === 205) ||\n (resStatus === 304)\n\n if (isEmpty) {\n return res\n }\n\n const resHdr = res.headers\n const resOpt = {\n status: resStatus,\n statusText: res.statusText,\n headers: resHdr,\n }\n\n // fake redirect\n const isRedir =\n (resStatus === 311) ||\n (resStatus === 312) ||\n (resStatus === 317) ||\n (resStatus === 318)\n\n if (isRedir) {\n const newUrl = resHdr.get('location')\n if (newUrl) {\n // 重定向到相对路径,是基于请求的 URL 计算(不是页面的 URL)\n const u = new URL(newUrl, urlObj)\n if (req.redirect === 'follow') {\n if (redirNum > 5) {\n return new Response('TOO_MUCH_REDIR')\n }\n return forward(req, u, redirNum + 1)\n }\n _urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"encUrlObj\"](u)\n // urlx.delFlag(u)\n resOpt.headers = new Headers(resHdr)\n resOpt.headers.set('location', u)\n }\n resOpt.status = resStatus - 10\n return new Response(res.body, resOpt)\n }\n\n if (reqType === 0) {\n return res\n }\n\n // content-type: text/html; ...; charset=\"gbk\"\n const ctVal = resHdr.get('content-type') || ''\n const [, mime, charset] = ctVal\n .toLocaleLowerCase()\n .match(/([^;]*)(?:.*?charset=['\"]?([^'\"]+))?/)\n\n // if (charset && !util.isUtf8(charset)) {\n // console.warn('[jsproxy] charset:', charset, urlObj.href)\n // }\n\n if (reqType === TYPE_HTML) {\n if (mime === 'text/html') {\n return _inject_js__WEBPACK_IMPORTED_MODULE_2__[\"htmlRemote\"](res, resOpt)\n }\n } else if (reqType === TYPE_JS) {\n return _inject_js__WEBPACK_IMPORTED_MODULE_2__[\"jsRemote\"](res, resOpt, charset)\n }\n return res\n}\n\n\nasync function proxy(e, urlObj) {\n // TODO: 读取本地缓存的资源,以及从本地 CDN 加速\n try {\n return await forward(e.request, urlObj)\n } catch (err) {\n console.warn('[jsproxy] forward err:', err)\n }\n}\n\n\nself.onfetch = function(e) {\n const u = new URL(e.request.url)\n\n // internal resource (helper.js)\n if (_urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"isMyRootHost\"](u.host)) {\n return\n }\n if (_urlx_js__WEBPACK_IMPORTED_MODULE_0__[\"isHttpProto\"](u.protocol)) {\n e.respondWith(proxy(e, u))\n } else {\n console.log('ignore non-http res:', u.href)\n }\n}\n\n\nself.onactivate = function() {\n\tclients.claim()\n}\n\nconsole.log('[jsproxy] sw inited')\n\n\n//# sourceURL=webpack:///./src/sw.js?");
- /***/ }),
- /***/ "./src/urlx.js":
- /*!*********************!*\
- !*** ./src/urlx.js ***!
- \*********************/
- /*! exports provided: getMyRootHost, getMyExtHost, encHost, decHost, isMyHost, isMyRootHost, isMySubHost, isMyExtHost, isHttpProto, encUrlObj, decUrlObj, encUrlStr, decUrlStr, pack, unpack */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getMyRootHost\", function() { return getMyRootHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"getMyExtHost\", function() { return getMyExtHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"encHost\", function() { return encHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decHost\", function() { return decHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isMyHost\", function() { return isMyHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isMyRootHost\", function() { return isMyRootHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isMySubHost\", function() { return isMySubHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isMyExtHost\", function() { return isMyExtHost; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isHttpProto\", function() { return isHttpProto; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"encUrlObj\", function() { return encUrlObj; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decUrlObj\", function() { return decUrlObj; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"encUrlStr\", function() { return encUrlStr; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"decUrlStr\", function() { return decUrlStr; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pack\", function() { return pack; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"unpack\", function() { return unpack; });\n/* harmony import */ var _hostlist_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hostlist.js */ \"./src/hostlist.js\");\n\n\nconst MY_ROOT_DOT = '.' + _hostlist_js__WEBPACK_IMPORTED_MODULE_0__[\"MY_ROOT\"]\nconst MY_EXT = 'ext' + MY_ROOT_DOT\nconst MY_EXT_DOT = '.' + MY_EXT\n\nconst HOST_ENC_MAP = {}\nconst HOST_DEC_MAP = {}\n\n_hostlist_js__WEBPACK_IMPORTED_MODULE_0__[\"HOST_LIST\"].forEach(([alias, rHost]) => {\n HOST_ENC_MAP[rHost] = alias\n HOST_DEC_MAP[alias] = rHost\n})\n\n\nfunction getMyRootHost() {\n return _hostlist_js__WEBPACK_IMPORTED_MODULE_0__[\"MY_ROOT\"]\n}\n\nfunction getMyExtHost() {\n return MY_EXT\n}\n\nfunction makeReg(tmpl, map, suffix = '') {\n const list = Object.keys(map)\n .join('|')\n .replace(/\\./g, '\\\\.')\n\n const [a, b, c] = tmpl.raw\n if (suffix) {\n suffix = suffix.replace(/\\./g, '\\\\.') + c\n }\n return RegExp(a + list + b + suffix)\n}\n\nconst R_HOST_ENC = makeReg`^([\\w-]+\\.)??(${HOST_ENC_MAP})$`\nconst R_HOST_DEC = makeReg`^([\\w-]+\\.)??(${HOST_DEC_MAP})${MY_ROOT_DOT}$`\n\n\n/**\n * encode host (rHost to vHost)\n * \n * @param {string} rHost\n * @example\n * 'twitter.com' -> 'tw.mysite.net'\n * 'www.google.com' -> 'www.gg.mysite.net'\n * 'www.google.com.hk' -> 'www.gk.mysite.net'\n * 'unsupport.com' -> 'unsupport-dot-com.mysite.net'\n * 'not-support.com' -> 'not-support-dot-com.mysite.net'\n * '*.mysite.net' -> '*.mysite.net'\n * 'mysite.net' -> 'mysite.net'\n */\nfunction _encHost(rHost) {\n if (isMyHost(rHost)) {\n return rHost\n }\n // 内置域名(替换成短别名)\n const m = rHost.match(R_HOST_ENC)\n if (m) {\n const [, sub, root] = m\n const vHost = HOST_ENC_MAP[root]\n if (vHost) {\n return (sub || '') + vHost + MY_ROOT_DOT\n }\n }\n // 外置域名(将 `.` 替换成 `-dot-`)\n if (rHost.includes('-dot-')) {\n console.warn('invalid host:', rHost)\n return rHost\n }\n return rHost.replace(/\\./g, '-dot-') + MY_EXT_DOT\n}\n\n/**\n * decode host (vHost to rHost)\n * \n * @param {string} vHost\n * @returns {string}\n * return *null* if vHost not ends with `HOST_SUFFIX`\n * or not in `HOST_LIST`\n * \n * @example\n * 'gg.mysite.net' -> 'google.com'\n * 'www.gg.mysite.net' -> 'www.google.com'\n * 'not-support-dot-com.mysite.net' -> 'not-support.com'\n * 'www-dot-mysite-dot-net.mysite.net' -> 'www.mysite.net'\n * 'www.google.com' -> null\n * 'x.mysite.net' -> null\n */\nfunction _decHost(vHost) {\n if (isMyExtHost(vHost)) {\n return vHost\n .slice(0, -MY_EXT_DOT.length)\n .replace(/-dot-/g, '.')\n }\n const m = vHost.match(R_HOST_DEC)\n if (m) {\n const [, sub, root] = m\n const rHost = HOST_DEC_MAP[root]\n if (rHost) {\n return (sub || '') + rHost\n }\n }\n return null\n}\n\nconst encCache = {}\nconst decCache = {}\n\n/**\n * @param {string} rHost \n */\nfunction encHost(rHost) {\n let ret = encCache[rHost]\n if (!ret) {\n ret = _encHost(rHost)\n encCache[rHost] = ret\n }\n return ret\n}\n\nfunction decHost(vHost) {\n let ret = decCache[vHost]\n if (!ret) {\n ret = _decHost(vHost)\n decCache[vHost] = ret\n }\n return ret\n}\n\n\n/**\n * @param {string} host \n */\nfunction isMyHost(host) {\n return isMyRootHost(host) || isMySubHost(host)\n}\n\n/**\n * @param {string} host \n */\nfunction isMyRootHost(host) {\n return host === _hostlist_js__WEBPACK_IMPORTED_MODULE_0__[\"MY_ROOT\"]\n}\n\n/**\n * @param {string} host \n */\nfunction isMySubHost(host) {\n return host.endsWith(MY_ROOT_DOT)\n}\n\n/**\n * @param {string} host \n */\nfunction isMyExtHost(host) {\n return host.endsWith(MY_EXT_DOT)\n}\n\n\n/**\n * @param {string} path \n */\nfunction isHttpProto(path) {\n return /^https?:/.test(path)\n}\n\n\n/**\n * encode urlObj.hostname to vHost\n * \n * @param {URL} urlObj\n */\nfunction encUrlObj(urlObj) {\n urlObj.hostname = encHost(urlObj.hostname)\n}\n\n\n/**\n * @param {URL} urlObj\n * @returns {boolean}\n */\nfunction decUrlObj(urlObj) {\n const host = decHost(urlObj.hostname)\n if (host) {\n urlObj.hostname = host\n }\n return !!host\n}\n\n\n/**\n * @param {string} url \n * 需编码的 URL 字符串,可以是完整 URL,或相对路径、相对协议。\n * \n * @param {string | URL} baseUrl\n * 如果 url 不完整,需指定一个基地址。\n * 如果未指定基地址,并且 url 不完整,则返回 url 本身。\n */\nfunction encUrlStr(url, baseUrl) {\n if (!url) {\n return url\n }\n try {\n var urlObj = new URL(url, baseUrl)\n } catch (err) {\n return url\n }\n encUrlObj(urlObj)\n return urlObj.href\n}\n\n\n/**\n * @param {string} url \n */\nfunction decUrlStr(url) {\n if (!url) {\n return url\n }\n try {\n var urlObj = new URL(url)\n } catch (err) {\n return url\n }\n return decUrlObj(urlObj) ? urlObj.href : url\n}\n\n\n/**\n * @param {URL} urlObj\n * @param {boolean} hasSw\n * @param {boolean} hasCors\n */\nfunction pack(urlObj, hasSw, hasCors) {\n let unsafe = false\n\n switch (urlObj.protocol) {\n case 'https:':\n break\n case 'wss:':\n break\n case 'http:':\n unsafe = true\n urlObj.protocol = 'https:'\n break\n case 'ws:':\n unsafe = true\n urlObj.protocol = 'wss:'\n break\n default:\n // 例如 chrome-extension:\n return\n }\n\n encUrlObj(urlObj)\n\n const port = urlObj.port\n\n // 都未设置,则不加 flag\n if (!hasSw && !unsafe && !hasCors && !port) {\n return\n }\n\n if (port && port !== '443') {\n urlObj.port = '443'\n }\n\n let flag = '' +\n (+hasSw) +\n (+unsafe) +\n (+hasCors) +\n port\n\n //\n // 使用 urlObj.searchParams 设置参数会对已有参数进行编码,例如:\n // new URL('https://s.yimg.com/zz/combo?yui:/3.12.0/yui/yui-min.js')\n // 设置参数后 :/ 等字符会被编码,导致资源无法加载。\n //\n let args = urlObj.search\n\n urlObj.search = args.replace(/&flag__=[^&]*|$/, _ => {\n // 出现 ?&flag= 也没事,后端用同样的方法删除该标记\n return (args ? '' : '?') + '&flag__=' + flag\n })\n}\n\n// /**\n// * @param {URL} urlObj \n// */\n// export function delFlag(urlObj) {\n// urlObj.search = urlObj.search.replace(/&flag__=[^&]*/, '')\n// }\n\n/**\n * @param {URL} urlObj \n */\nfunction unpack(urlObj) {\n const flag = urlObj.searchParams.get('flag__')\n if (!flag) {\n return\n }\n const unsafe = (flag[1] === '1')\n const port = flag.substr(3)\n\n switch (urlObj.protocol) {\n case 'https:':\n if (unsafe) {\n urlObj.protocol = 'http:'\n }\n break\n case 'wss:':\n if (unsafe) {\n urlObj.protocol = 'ws:'\n }\n break\n default:\n console.warn('unpack:', urlObj)\n return\n }\n if (port) {\n urlObj.port = port\n }\n\n decUrlObj(urlObj)\n}\n\n//# sourceURL=webpack:///./src/urlx.js?");
- /***/ }),
- /***/ "./src/util.js":
- /*!*********************!*\
- !*** ./src/util.js ***!
- \*********************/
- /*! exports provided: strToBytes, bytesToStr, isUtf8 */
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
- "use strict";
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"strToBytes\", function() { return strToBytes; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bytesToStr\", function() { return bytesToStr; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isUtf8\", function() { return isUtf8; });\nconst ENC = new TextEncoder()\n\n/**\n * @param {string} str \n */\nfunction strToBytes(str) {\n return ENC.encode(str)\n}\n\n/**\n * @param {BufferSource} bytes \n * @param {string} charset \n */\nfunction bytesToStr(bytes, charset = 'utf-8') {\n return new TextDecoder(charset).decode(bytes)\n}\n\n/**\n * @param {string} label \n */\nfunction isUtf8(label) {\n return /^utf-?8$/i.test(label)\n}\n\n\n//# sourceURL=webpack:///./src/util.js?");
- /***/ }),
- /***/ "./src/worker.js":
- /*!***********************!*\
- !*** ./src/worker.js ***!
- \***********************/
- /*! no static exports found */
- /***/ (function(module, exports) {
- eval("\n\n//# sourceURL=webpack:///./src/worker.js?");
- /***/ })
- /******/ });
|