|
1 | | -(function (exports) { |
| 1 | +(function (global) { |
2 | 2 | "use strict"; |
3 | | - /*global unblocker*/ |
4 | 3 |
|
5 | 4 | // todo: |
6 | | - // - fetch |
7 | | - // - history API |
8 | 5 | // - postMessage |
9 | 6 | // - open |
10 | 7 | // - split each part into separate files (?) |
11 | 8 | // - wrap other JS and provide proxies to fix writes to window.location and document.cookie |
12 | 9 | // - will require updating contentTypes.html.includes(data.contentType) to include js |
13 | 10 | // - that, in turn will require decompressing js.... |
14 | 11 |
|
15 | | - console.log("begin unblocker client scripts", unblocker); |
16 | | - |
17 | | - function fixUrl(target, prefix, location) { |
18 | | - var currentRemoteHref = |
19 | | - location.pathname.substr(prefix.length) + location.search + location.hash; |
20 | | - var url = new URL(target, currentRemoteHref); |
| 12 | + function fixUrl(urlStr, config, location) { |
| 13 | + var currentRemoteHref; |
| 14 | + if (location.pathname.substr(0, config.prefix.length) === config.prefix) { |
| 15 | + currentRemoteHref = |
| 16 | + location.pathname.substr(config.prefix.length) + |
| 17 | + location.search + |
| 18 | + location.hash; |
| 19 | + } else { |
| 20 | + // in case sites like youtube bypass our history wrapper somehow |
| 21 | + currentRemoteHref = config.url; |
| 22 | + } |
| 23 | + var url = new URL(urlStr, currentRemoteHref); |
21 | 24 |
|
22 | 25 | //todo: handle already proxied urls (will be important for checking current dom) |
23 | 26 |
|
24 | 27 | // don't break data: urls |
25 | 28 | if (url.protocol === "data:") { |
26 | | - return target; |
| 29 | + return urlStr; |
27 | 30 | } |
28 | 31 |
|
29 | 32 | // sometimes websites are tricky |
|
36 | 39 | url.protocol = currentRemoteUrl.protocol; |
37 | 40 | // todo: handle websocket protocols |
38 | 41 | } |
39 | | - return prefix + url.href; |
| 42 | + return config.prefix + url.href; |
40 | 43 | } |
41 | 44 |
|
42 | | - function initXMLHttpRequest(config) { |
43 | | - var _XMLHttpRequest = XMLHttpRequest; |
| 45 | + function initXMLHttpRequest(config, window) { |
| 46 | + if (typeof window.XMLHttpRequest === "undefined") return; |
| 47 | + var _XMLHttpRequest = window.XMLHttpRequest; |
44 | 48 |
|
45 | 49 | window.XMLHttpRequest = function (opts) { |
46 | 50 | var xhr = new _XMLHttpRequest(opts); |
47 | 51 | var _open = xhr.open; |
48 | 52 | xhr.open = function () { |
49 | 53 | var args = Array.prototype.slice.call(arguments); |
50 | | - args[1] = fixUrl(args[1], config.prefix, location); |
| 54 | + args[1] = fixUrl(args[1], config, location); |
51 | 55 | return _open.apply(xhr, args); |
52 | 56 | }; |
53 | 57 | return xhr; |
54 | 58 | }; |
55 | 59 | } |
56 | 60 |
|
57 | | - function initCreateElement(config) { |
58 | | - var prefix = config.prefix; |
59 | | - var _createElement = document.createElement; |
| 61 | + function initFetch(config, window) { |
| 62 | + if (typeof window.fetch === "undefined") return; |
| 63 | + var _fetch = window.fetch; |
60 | 64 |
|
61 | | - document.createElement = function (tagName, options) { |
62 | | - var element = _createElement.call(document, tagName, options); |
| 65 | + window.fetch = function (resource, init) { |
| 66 | + if (resource.url) { |
| 67 | + resource.url = fixUrl(resource.url, config, location); |
| 68 | + } else { |
| 69 | + resource = fixUrl(resource.toString(), config, location); |
| 70 | + } |
| 71 | + return _fetch(resource, init); |
| 72 | + }; |
| 73 | + } |
| 74 | + |
| 75 | + function initCreateElement(config, window) { |
| 76 | + if ( |
| 77 | + typeof window.document === "undefined" || |
| 78 | + typeof window.document.createElement === "undefined" |
| 79 | + ) |
| 80 | + return; |
| 81 | + var _createElement = window.document.createElement; |
| 82 | + |
| 83 | + window.document.createElement = function (tagName, options) { |
| 84 | + var element = _createElement.call(window.document, tagName, options); |
63 | 85 | // todo: whitelist elements with href or src attributes and only check those |
64 | 86 | setTimeout(function () { |
65 | 87 | if (element.src) { |
66 | | - element.src = fixUrl(element.src, prefix, location); |
| 88 | + element.src = fixUrl(element.src, config, location); |
67 | 89 | } |
68 | 90 | if (element.href) { |
69 | | - element.href = fixUrl(element.href, prefix, location); |
| 91 | + element.href = fixUrl(element.href, config, location); |
70 | 92 | } |
71 | 93 | // todo: support srcset and ..? |
72 | 94 | }, 0); |
|
75 | 97 | }; |
76 | 98 | } |
77 | 99 |
|
78 | | - function initWebsockets(config) { |
79 | | - var _WebSocket = WebSocket; |
| 100 | + // js on some sites, such as youtube, uses an iframe to grab native APIs such as history, so we need to fix those also. |
| 101 | + // the |
| 102 | + // function initBodyAppendiFrame(config, window) { |
| 103 | + // if ( |
| 104 | + // typeof window.document === "undefined" || |
| 105 | + // typeof window.document.body === "undefined" |
| 106 | + // ) |
| 107 | + // if (tagName.toLowerCase() === 'iframe' && element.contentWindow) { |
| 108 | + // // todo: check if we need to wait for onLoad or whatever |
| 109 | + // initForWindow(config, element.contentWindow); |
| 110 | + // } |
| 111 | + // } |
| 112 | + |
| 113 | + function initWebsockets(config, window) { |
| 114 | + if (typeof window.WebSocket === "undefined") return; |
| 115 | + var _WebSocket = window.WebSocket; |
80 | 116 | var prefix = config.prefix; |
81 | 117 | var proxyHost = location.host; |
82 | 118 | var isSecure = location.protocol === "https"; |
|
118 | 154 | }; |
119 | 155 | } |
120 | 156 |
|
121 | | - initXMLHttpRequest(unblocker); |
122 | | - initCreateElement(unblocker); |
123 | | - initWebsockets(unblocker); |
| 157 | + // todo: figure out how youtube bypasses this |
| 158 | + // notes: look at bindHistoryStateFunctions_ - it looks like it checks the contentWindow.history of an iframe *fitst*, then it's __proto__, then the global history api |
| 159 | + // - so, we need to inject this into iframes also |
| 160 | + function initPushState(config, window) { |
| 161 | + if ( |
| 162 | + typeof window.history === "undefined" || |
| 163 | + typeof window.history.pushState === "undefined" |
| 164 | + ) |
| 165 | + return; |
| 166 | + |
| 167 | + var _pushState = window.history.pushState; |
| 168 | + window.history.pushState = function (state, title, url) { |
| 169 | + if (url) { |
| 170 | + url = fixUrl(url, config, location); |
| 171 | + config.url = new URL(url, config.url); |
| 172 | + return _pushState.call(history, state, title, url); |
| 173 | + } |
| 174 | + }; |
| 175 | + |
| 176 | + if (typeof window.history.replaceState === "undefined") return; |
| 177 | + var _replaceState = window.history.replaceState; |
| 178 | + window.history.replaceState = function (state, title, url) { |
| 179 | + if (url) { |
| 180 | + url = fixUrl(url, config, location); |
| 181 | + config.url = new URL(url, config.url); |
| 182 | + return _replaceState.call(history, state, title, url); |
| 183 | + } |
| 184 | + }; |
| 185 | + } |
124 | 186 |
|
125 | | - console.log("unblocker client scripts initialized"); |
| 187 | + function initForWindow(unblocker, window) { |
| 188 | + console.log("begin unblocker client scripts", unblocker, window); |
| 189 | + initXMLHttpRequest(unblocker, window); |
| 190 | + initFetch(unblocker, window); |
| 191 | + initCreateElement(unblocker, window); |
| 192 | + initWebsockets(unblocker, window); |
| 193 | + initPushState(unblocker, window); |
| 194 | + if (window === global) { |
| 195 | + // leave no trace |
| 196 | + delete global.unblockerInit; |
| 197 | + } |
| 198 | + console.log("unblocker client scripts initialized"); |
| 199 | + } |
126 | 200 |
|
127 | | - // export things for testing if loaded via commonjs |
128 | | - exports.fixUrl = fixUrl; |
| 201 | + // either export things for testing or put the init method into the global scope to be called |
| 202 | + // with config by the next script tag in a browser |
129 | 203 | /*globals module*/ |
130 | | -})((typeof module === "object" && module.exports) || {}); |
| 204 | + if (typeof module === "undefined") { |
| 205 | + global.unblockerInit = initForWindow; |
| 206 | + } else { |
| 207 | + module.exports = { |
| 208 | + initForWindow: initForWindow, |
| 209 | + fixUrl: fixUrl, |
| 210 | + }; |
| 211 | + } |
| 212 | +})(this); // window in a browser, global in node.js |
0 commit comments