├── README.md ├── test ├── index.html └── nodejs │ └── servers.js ├── v2.5 ├── _locales │ └── en │ │ └── messages.json ├── _metadata │ └── generated_indexed_rulesets │ │ ├── _ruleset1 │ │ ├── _ruleset2 │ │ ├── _ruleset3 │ │ ├── _ruleset4 │ │ ├── _ruleset5 │ │ ├── _ruleset6 │ │ └── _ruleset7 ├── context.js ├── data │ └── icons │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 19.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 38.png │ │ ├── 48.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── disabled │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 19.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 38.png │ │ ├── 48.png │ │ ├── 512.png │ │ └── 64.png ├── manifest.json ├── rulesets │ ├── allow-credentials.json │ ├── allow-headers.json │ ├── allow-shared-array-buffer.json │ ├── csp.json │ ├── overwrite-origin.json │ ├── referer.json │ └── x-frame.json ├── v2.js └── worker.js ├── v2 ├── background.js ├── data │ └── icons │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 19.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 38.png │ │ ├── 48.png │ │ ├── 512.png │ │ ├── 64.png │ │ └── disabled │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 19.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 38.png │ │ ├── 48.png │ │ ├── 512.png │ │ └── 64.png └── manifest.json └── v3 ├── _locales └── en │ └── messages.json ├── data ├── debug │ ├── index.css │ ├── index.html │ └── index.js └── icons │ ├── 128.png │ ├── 16.png │ ├── 19.png │ ├── 256.png │ ├── 32.png │ ├── 38.png │ ├── 48.png │ ├── 512.png │ ├── 64.png │ └── disabled │ ├── 128.png │ ├── 16.png │ ├── 19.png │ ├── 256.png │ ├── 32.png │ ├── 38.png │ ├── 48.png │ ├── 512.png │ └── 64.png ├── manifest.json └── worker.js /README.md: -------------------------------------------------------------------------------- 1 | This extension provides control over "XMLHttpRequest" and "fetch" methods by providing custom "access-control-allow-origin" and "access-control-allow-methods" headers to every requests that the browser receives. A user can toggle the extension on and off from the toolbar button. To modify how these headers are altered, use the right-click context menu items. You can customize what methods are allowed. The default option is to allow 'GET', 'PUT', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH' methods. You can also ask the extension not to overwrite these headers when the server already fills them. This extension also fixes CORS policies of redirected URLs. 2 | 3 | ### YouTube Preview 4 | [![YouTube Preview](https://img.youtube.com/vi/8berLeTjKDM/0.jpg)](https://www.youtube.com/watch?v=8berLeTjKDM) 5 | 6 | ### Links: 7 | * FAQs page: https://add0n.com/access-control.html 8 | * Chrome Webstore: https://chrome.google.com/webstore/detail/cors-unblock/lfhmikememgdcahcdlaciloancbhjino/ 9 | * Firefox add-ons: https://addons.mozilla.org/en-US/firefox/addon/cors-unblock/ 10 | * Edge addons: https://microsoftedge.microsoft.com/addons/detail/cors-unblock/hkjklmhkbkdhlgnnfbbcihcajofmjgbh 11 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test Page 5 | 6 | 7 | 8 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/nodejs/servers.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | { 4 | const server = http.createServer((req, res) => { 5 | res.statusCode = 200; 6 | res.setHeader('Content-Type', 'text/HTML'); 7 | res.end(` 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 26 | 27 | 28 | 29 | `); 30 | }); 31 | 32 | server.listen(3000, '127.0.0.1', () => { 33 | console.log('Main server is ready!', '127.0.0.1:3000'); 34 | }); 35 | } 36 | { 37 | const server = http.createServer((req, res) => { 38 | res.statusCode = 200; 39 | res.setHeader('Content-Type', 'text/TEXT'); 40 | res.end(`hello`); 41 | }); 42 | 43 | server.listen(3001, '127.0.0.1', () => { 44 | console.log('API server is ready!', '127.0.0.1:3001'); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /v2.5/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": { 3 | "message": "No more CORS error by appending 'Access-Control-Allow-Origin: *' header to local and remote web requests when enabled" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset1 -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset2 -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset3 -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset4 -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset5 -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset6 -------------------------------------------------------------------------------- /v2.5/_metadata/generated_indexed_rulesets/_ruleset7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/_metadata/generated_indexed_rulesets/_ruleset7 -------------------------------------------------------------------------------- /v2.5/context.js: -------------------------------------------------------------------------------- 1 | /* global notify */ 2 | { 3 | const once = () => chrome.storage.local.get({ 4 | 'overwrite-origin': true, 5 | 'allow-credentials': true, 6 | 'allow-headers': false, 7 | 'remove-csp': false, 8 | 'allow-shared-array-buffer': false, 9 | 'remove-referer': false, 10 | 'fix-origin': false, 11 | 'remove-x-frame': true, 12 | 'unblock-initiator': true, 13 | 'fake-supported-methods': true, 14 | 'methods': self.DEFAULT_METHODS, 15 | 'status-code-methods': self.DEFAULT_STATUS_METHODS 16 | }, prefs => { 17 | chrome.contextMenus.create({ 18 | title: 'Test CORS', 19 | id: 'test-cors', 20 | contexts: ['browser_action'] 21 | }, () => chrome.runtime.lastError); 22 | chrome.contextMenus.create({ 23 | title: 'Usage Instruction', 24 | id: 'tutorial', 25 | contexts: ['browser_action'] 26 | }, () => chrome.runtime.lastError); 27 | chrome.contextMenus.create({ 28 | title: 'Enable Access-Control-Allow-Origin', 29 | type: 'checkbox', 30 | id: 'overwrite-origin', 31 | contexts: ['browser_action'], 32 | checked: prefs['overwrite-origin'] 33 | }, () => chrome.runtime.lastError); 34 | chrome.contextMenus.create({ 35 | title: 'Enable Access-Control-Allow-Credentials', 36 | type: 'checkbox', 37 | id: 'allow-credentials', 38 | contexts: ['browser_action'], 39 | checked: prefs['allow-credentials'] 40 | }, () => chrome.runtime.lastError); 41 | chrome.contextMenus.create({ 42 | title: 'Enable Access-Control-[Allow/Expose]-Headers', 43 | type: 'checkbox', 44 | id: 'allow-headers', 45 | contexts: ['browser_action'], 46 | checked: prefs['allow-headers'] 47 | }, () => chrome.runtime.lastError); 48 | chrome.contextMenus.create({ 49 | id: 'extra', 50 | title: 'Extra Options', 51 | contexts: ['browser_action'] 52 | }, () => chrome.runtime.lastError); 53 | chrome.contextMenus.create({ 54 | title: 'Remove X-Frame-Options', 55 | type: 'checkbox', 56 | id: 'remove-x-frame', 57 | contexts: ['browser_action'], 58 | checked: prefs['remove-x-frame'], 59 | parentId: 'extra' 60 | }, () => chrome.runtime.lastError); 61 | chrome.contextMenus.create({ 62 | title: 'Remove "Content-Security-Policy" Headers', 63 | type: 'checkbox', 64 | id: 'remove-csp', 65 | contexts: ['browser_action'], 66 | checked: prefs['remove-csp'], 67 | parentId: 'extra' 68 | }, () => chrome.runtime.lastError); 69 | chrome.contextMenus.create({ 70 | title: 'Append Headers to Allow Shared Array Buffer', 71 | type: 'checkbox', 72 | id: 'allow-shared-array-buffer', 73 | contexts: ['browser_action'], 74 | checked: prefs['allow-shared-array-buffer'], 75 | parentId: 'extra' 76 | }, () => chrome.runtime.lastError); 77 | chrome.contextMenus.create({ 78 | id: 'referer', 79 | title: 'Add/Remove "referer" and "origin" Headers', 80 | contexts: ['browser_action'], 81 | parentId: 'extra' 82 | }, () => chrome.runtime.lastError); 83 | chrome.contextMenus.create({ 84 | title: 'Add same-origin "referer" and "origin" Headers', 85 | type: 'checkbox', 86 | id: 'fix-origin', 87 | contexts: ['browser_action'], 88 | checked: prefs['fix-origin'], 89 | parentId: 'referer' 90 | }, () => chrome.runtime.lastError); 91 | chrome.contextMenus.create({ 92 | title: 'Remove "referer" and "origin" Headers', 93 | type: 'checkbox', 94 | id: 'remove-referer', 95 | contexts: ['browser_action'], 96 | checked: prefs['remove-referer'], 97 | parentId: 'referer' 98 | }, () => chrome.runtime.lastError); 99 | chrome.contextMenus.create({ 100 | title: `Only Unblock Request's Initiator`, 101 | type: 'checkbox', 102 | id: 'unblock-initiator', 103 | contexts: ['browser_action'], 104 | checked: prefs['unblock-initiator'], 105 | parentId: 'extra' 106 | }, () => chrome.runtime.lastError); 107 | chrome.contextMenus.create({ 108 | title: 'Pretend Enabled Methods are Supported by Server', 109 | type: 'checkbox', 110 | id: 'fake-supported-methods', 111 | contexts: ['browser_action'], 112 | checked: prefs['fake-supported-methods'], 113 | parentId: 'extra' 114 | }, () => chrome.runtime.lastError); 115 | chrome.contextMenus.create({ 116 | title: 'Access-Control-Allow-Methods Methods:', 117 | contexts: ['browser_action'], 118 | parentId: 'extra', 119 | id: 'menu' 120 | }, () => chrome.runtime.lastError); 121 | for (const method of self.DEFAULT_METHODS) { 122 | if (['GET', 'POST', 'HEAD'].includes(method)) { 123 | continue; 124 | } 125 | chrome.contextMenus.create({ 126 | title: method, 127 | type: 'checkbox', 128 | id: method, 129 | contexts: ['browser_action'], 130 | checked: prefs.methods.includes(method), 131 | parentId: 'menu' 132 | }, () => chrome.runtime.lastError); 133 | } 134 | chrome.contextMenus.create({ 135 | title: 'Overwrite 4xx Status Code For This Tab', 136 | contexts: ['browser_action'], 137 | parentId: 'extra', 138 | id: 'status-code', 139 | enabled: Boolean(chrome.debugger) 140 | }, () => chrome.runtime.lastError); 141 | chrome.contextMenus.create({ 142 | title: 'Enable on This Tab', 143 | contexts: ['browser_action'], 144 | parentId: 'status-code', 145 | id: 'status-code-enable' 146 | }, () => chrome.runtime.lastError); 147 | chrome.contextMenus.create({ 148 | title: 'Disable on This Tab', 149 | contexts: ['browser_action'], 150 | parentId: 'status-code', 151 | id: 'status-code-disable' 152 | }, () => chrome.runtime.lastError); 153 | chrome.contextMenus.create({ 154 | title: 'Overwrite 4xx Status Code Methods', 155 | contexts: ['browser_action'], 156 | parentId: 'extra', 157 | id: 'status-code-methods', 158 | enabled: Boolean(chrome.debugger) 159 | }, () => chrome.runtime.lastError); 160 | for (const method of self.DEFAULT_STATUS_METHODS) { 161 | chrome.contextMenus.create({ 162 | title: method, 163 | type: 'checkbox', 164 | id: 'status-code-methods-' + method, 165 | contexts: ['browser_action'], 166 | checked: prefs['status-code-methods'].includes(method), 167 | parentId: 'status-code-methods' 168 | }, () => chrome.runtime.lastError); 169 | } 170 | }); 171 | chrome.runtime.onStartup.addListener(once); 172 | chrome.runtime.onInstalled.addListener(once); 173 | } 174 | 175 | const debug = async (source, method, params) => { 176 | if (method === 'Fetch.requestPaused') { 177 | const opts = { 178 | requestId: params.requestId 179 | }; 180 | const status = params.responseStatusCode; 181 | if (status && status >= 400 && status < 500) { 182 | const methods = await new Promise(resolve => chrome.storage.local.get({ 183 | 'status-code-methods': self.DEFAULT_STATUS_METHODS 184 | }, prefs => resolve(prefs['status-code-methods']))); 185 | 186 | const method = params.request?.method; 187 | if (method && methods.includes(method)) { 188 | opts.responseCode = 200; 189 | opts.responseHeaders = params.responseHeaders || []; 190 | } 191 | } 192 | 193 | if (chrome.debugger) { 194 | chrome.debugger.sendCommand({ 195 | tabId: source.tabId 196 | }, 'Fetch.continueResponse', opts); 197 | } 198 | } 199 | }; 200 | 201 | chrome.contextMenus.onClicked.addListener(({menuItemId, checked}, tab) => { 202 | if (menuItemId === 'status-code-enable' || menuItemId === 'status-code-disable') { 203 | chrome.debugger.onEvent.removeListener(debug); 204 | chrome.debugger.onEvent.addListener(debug); 205 | 206 | if (menuItemId === 'status-code-enable') { 207 | chrome.storage.local.get({ 208 | enabled: false 209 | }, prefs => { 210 | if (prefs.enabled) { 211 | const target = { 212 | tabId: tab.id 213 | }; 214 | 215 | chrome.debugger.attach(target, '1.2', () => { 216 | const {lastError} = chrome.runtime; 217 | if (lastError) { 218 | console.warn(lastError); 219 | notify(lastError.message); 220 | } 221 | else { 222 | chrome.debugger.sendCommand(target, 'Fetch.enable', { 223 | patterns: [{ 224 | requestStage: 'Response' 225 | }] 226 | }); 227 | } 228 | }); 229 | } 230 | else { 231 | notify('To overwrite status codes, enable the extension first'); 232 | } 233 | }); 234 | } 235 | else { 236 | chrome.debugger.detach({ 237 | tabId: tab.id 238 | }, () => chrome.runtime.lastError); 239 | } 240 | } 241 | else if (menuItemId.startsWith('status-code-methods-')) { 242 | chrome.storage.local.get({ 243 | 'status-code-methods': self.DEFAULT_STATUS_METHODS 244 | }, prefs => { 245 | const methods = new Set(prefs['status-code-methods']); 246 | const method = menuItemId.replace('status-code-methods-', ''); 247 | 248 | methods[checked ? 'add' : 'delete'](method); 249 | chrome.storage.local.set({ 250 | 'status-code-methods': [...methods] 251 | }); 252 | }); 253 | } 254 | else if (menuItemId === 'test-cors') { 255 | chrome.tabs.create({ 256 | url: 'https://webbrowsertools.com/test-cors/' 257 | }); 258 | } 259 | else if (menuItemId === 'tutorial') { 260 | chrome.tabs.create({ 261 | url: 'https://www.youtube.com/watch?v=8berLeTjKDM' 262 | }); 263 | } 264 | else if ( 265 | [ 266 | 'remove-csp', 267 | 'allow-shared-array-buffer', 268 | 'fix-origin', 'remove-referer', 269 | 'overwrite-origin', 'remove-x-frame', 'allow-credentials', 'allow-headers', 270 | 'unblock-initiator', 'fake-supported-methods' 271 | ].includes(menuItemId) 272 | ) { 273 | chrome.storage.local.set({ 274 | [menuItemId]: checked 275 | }); 276 | } 277 | else { 278 | chrome.storage.local.get({ 279 | methods: self.DEFAULT_METHODS 280 | }, prefs => { 281 | if (checked) { 282 | prefs.methods.push(menuItemId); 283 | } 284 | else { 285 | const index = prefs.methods.indexOf(menuItemId); 286 | if (index !== -1) { 287 | prefs.methods.splice(index, 1); 288 | } 289 | } 290 | chrome.storage.local.set(prefs); 291 | }); 292 | } 293 | }); 294 | 295 | if (chrome.debugger) { 296 | chrome.storage.onChanged.addListener(ps => { 297 | if (ps.enabled) { 298 | chrome.debugger.getTargets(os => { 299 | for (const o of os.filter(o => o.attached && o.type === 'page' && o.tabId)) { 300 | chrome.debugger.detach({ 301 | tabId: o.tabId 302 | }, () => chrome.runtime.lastError); 303 | } 304 | }); 305 | } 306 | }); 307 | } 308 | -------------------------------------------------------------------------------- /v2.5/data/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/128.png -------------------------------------------------------------------------------- /v2.5/data/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/16.png -------------------------------------------------------------------------------- /v2.5/data/icons/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/19.png -------------------------------------------------------------------------------- /v2.5/data/icons/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/256.png -------------------------------------------------------------------------------- /v2.5/data/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/32.png -------------------------------------------------------------------------------- /v2.5/data/icons/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/38.png -------------------------------------------------------------------------------- /v2.5/data/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/48.png -------------------------------------------------------------------------------- /v2.5/data/icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/512.png -------------------------------------------------------------------------------- /v2.5/data/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/64.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/128.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/16.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/19.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/256.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/32.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/38.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/48.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/512.png -------------------------------------------------------------------------------- /v2.5/data/icons/disabled/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2.5/data/icons/disabled/64.png -------------------------------------------------------------------------------- /v2.5/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "version": "0.3.8", 4 | "name": "CORS Unblock", 5 | "description": "__MSG_description__", 6 | "default_locale": "en", 7 | "permissions": [ 8 | "storage", 9 | "", 10 | "webRequest", 11 | "webRequestBlocking", 12 | "declarativeNetRequest", 13 | "contextMenus", 14 | "debugger" 15 | ], 16 | "declarative_net_request" : { 17 | "rule_resources" : [{ 18 | "id": "x-frame", 19 | "enabled": false, 20 | "path": "rulesets/x-frame.json" 21 | }, { 22 | "id": "overwrite-origin", 23 | "enabled": false, 24 | "path": "rulesets/overwrite-origin.json" 25 | }, { 26 | "id": "allow-credentials", 27 | "enabled": false, 28 | "path": "rulesets/allow-credentials.json" 29 | }, { 30 | "id": "allow-headers", 31 | "enabled": false, 32 | "path": "rulesets/allow-headers.json" 33 | }, { 34 | "id": "referer", 35 | "enabled": false, 36 | "path": "rulesets/referer.json" 37 | }, { 38 | "id": "csp", 39 | "enabled": false, 40 | "path": "rulesets/csp.json" 41 | }, { 42 | "id": "allow-shared-array-buffer", 43 | "enabled": false, 44 | "path": "rulesets/allow-shared-array-buffer.json" 45 | }] 46 | }, 47 | "icons": { 48 | "16": "/data/icons/16.png", 49 | "32": "/data/icons/32.png", 50 | "48": "/data/icons/48.png", 51 | "64": "/data/icons/64.png", 52 | "128": "/data/icons/128.png", 53 | "256": "/data/icons/256.png", 54 | "512": "/data/icons/512.png" 55 | }, 56 | "homepage_url": "https://webextension.org/listing/access-control.html", 57 | "browser_action":{}, 58 | "background": { 59 | "scripts": [ 60 | "worker.js", 61 | "context.js", 62 | "v2.js" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /v2.5/rulesets/allow-credentials.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "responseHeaders": [{ 7 | "operation": "set", 8 | "header": "Access-Control-Allow-Credentials", 9 | "value": "true" 10 | }] 11 | }, 12 | "condition": {} 13 | }] 14 | -------------------------------------------------------------------------------- /v2.5/rulesets/allow-headers.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "responseHeaders": [{ 7 | "operation": "set", 8 | "header": "Access-Control-Allow-Headers", 9 | "value": "*" 10 | }, { 11 | "operation": "set", 12 | "header": "Access-Control-Expose-Headers", 13 | "value": "*" 14 | }] 15 | }, 16 | "condition": { 17 | "excludedRequestMethods": ["options"] 18 | } 19 | }] 20 | -------------------------------------------------------------------------------- /v2.5/rulesets/allow-shared-array-buffer.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "responseHeaders": [{ 7 | "operation": "set", 8 | "header": "Cross-Origin-Opener-Policy", 9 | "value": "same-origin" 10 | }, { 11 | "operation": "set", 12 | "header": "Cross-Origin-Embedder-Policy", 13 | "value": "require-corp" 14 | }] 15 | }, 16 | "condition": { 17 | "resourceTypes": ["main_frame", "script", "image"] 18 | } 19 | }, { 20 | "id": 2, 21 | "priority": 1, 22 | "action": { 23 | "type": "modifyHeaders", 24 | "responseHeaders": [{ 25 | "operation": "set", 26 | "header": "Cross-Origin-Resource-Policy", 27 | "value": "cross-origin" 28 | }] 29 | }, 30 | "condition": {} 31 | }] 32 | -------------------------------------------------------------------------------- /v2.5/rulesets/csp.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "responseHeaders": [{ 7 | "operation": "remove", 8 | "header": "content-security-policy" 9 | }, { 10 | "operation": "remove", 11 | "header": "content-security-policy-report-only" 12 | }, { 13 | "operation": "remove", 14 | "header": "x-webkit-csp" 15 | }, { 16 | "operation": "remove", 17 | "header": "x-content-security-policy" 18 | }] 19 | }, 20 | "condition": { 21 | "resourceTypes": ["main_frame"] 22 | } 23 | }] 24 | -------------------------------------------------------------------------------- /v2.5/rulesets/overwrite-origin.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "responseHeaders": [{ 7 | "operation": "set", 8 | "header": "Access-Control-Allow-Origin", 9 | "value": "*" 10 | }] 11 | }, 12 | "condition": {} 13 | }] 14 | -------------------------------------------------------------------------------- /v2.5/rulesets/referer.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "requestHeaders": [{ 7 | "operation": "remove", 8 | "header": "referer" 9 | }] 10 | }, 11 | "condition": {} 12 | }] 13 | -------------------------------------------------------------------------------- /v2.5/rulesets/x-frame.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "priority": 1, 4 | "action": { 5 | "type": "modifyHeaders", 6 | "responseHeaders": [{ 7 | "operation": "remove", 8 | "header": "x-frame-options" 9 | }] 10 | }, 11 | "condition": { 12 | "resourceTypes": ["sub_frame"] 13 | } 14 | }] 15 | -------------------------------------------------------------------------------- /v2.5/v2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Tests: 4 | // https://mail.google.com/mail/u/0/#inbox 5 | // https://drive.google.com/drive/my-drive 6 | 7 | const v2 = {}; 8 | 9 | v2.headersReceived = d => { 10 | const {responseHeaders} = d; 11 | for (const o of v2.headersReceived.methods) { 12 | o.method(d); 13 | if (o.once) { 14 | v2.headersReceived.methods.delete(o); 15 | } 16 | } 17 | 18 | return { 19 | responseHeaders 20 | }; 21 | }; 22 | v2.headersReceived.methods = new Set(); 23 | 24 | v2.beforeSendHeaders = d => { 25 | const {requestHeaders} = d; 26 | for (const o of v2.beforeSendHeaders.methods) { 27 | o.method(d); 28 | if (o.once) { 29 | v2.beforeSendHeaders.methods.delete(o); 30 | } 31 | } 32 | 33 | return {requestHeaders}; 34 | }; 35 | v2.beforeSendHeaders.methods = new Set(); 36 | 37 | v2.install = prefs => { 38 | v2.prefs = prefs; 39 | 40 | const filters = ['blocking', 'responseHeaders']; 41 | if (/Firefox/.test(navigator.userAgent) === false) { 42 | filters.push('extraHeaders'); 43 | } 44 | 45 | chrome.webRequest.onHeadersReceived.removeListener(v2.headersReceived); 46 | chrome.webRequest.onHeadersReceived.addListener(v2.headersReceived, { 47 | urls: [''] 48 | }, filters); 49 | 50 | chrome.webRequest.onBeforeSendHeaders.removeListener(v2.beforeSendHeaders); 51 | 52 | const m = ['requestHeaders']; 53 | if (v2.prefs['fix-origin']) { 54 | m.push('blocking', 'extraHeaders'); 55 | } 56 | chrome.webRequest.onBeforeSendHeaders.addListener(v2.beforeSendHeaders, { 57 | urls: [''] 58 | }, m); 59 | }; 60 | v2.remove = () => { 61 | chrome.webRequest.onHeadersReceived.removeListener(v2.headersReceived); 62 | chrome.webRequest.onBeforeSendHeaders.removeListener(v2.beforeSendHeaders); 63 | }; 64 | 65 | // Access-Control-Allow-Headers for OPTIONS 66 | v2.beforeSendHeaders.methods.add({ 67 | method: d => { 68 | if (d.method === 'OPTIONS') { 69 | const r = d.requestHeaders.find(({name}) => name.toLowerCase() === 'access-control-request-headers'); 70 | 71 | if (r) { 72 | const {requestId} = d; 73 | 74 | v2.headersReceived.methods.add({ 75 | method: d => { 76 | if (d.method === 'OPTIONS' && d.requestId === requestId) { 77 | d.responseHeaders.push({ 78 | 'name': 'Access-Control-Allow-Headers', 79 | 'value': r.value 80 | }); 81 | } 82 | }, 83 | once: true 84 | }); 85 | } 86 | } 87 | } 88 | }); 89 | 90 | // Access-Control-Allow-Origin 91 | { 92 | const redirects = {}; 93 | chrome.tabs.onRemoved.addListener(tabId => delete redirects[tabId]); 94 | 95 | v2.headersReceived.methods.add({ 96 | method: d => { 97 | if (v2.prefs['overwrite-origin'] && d.type !== 'main_frame') { 98 | const {initiator, originUrl, responseHeaders} = d; 99 | let origin = '*'; 100 | 101 | if (v2.prefs['unblock-initiator'] || v2.prefs['allow-credentials']) { 102 | if (!redirects[d.tabId] || !redirects[d.tabId][d.requestId]) { 103 | try { 104 | const o = new URL(initiator || originUrl); 105 | origin = o.origin; 106 | } 107 | catch (e) {} 108 | } 109 | } 110 | if (d.statusCode === 301 || d.statusCode === 302) { 111 | redirects[d.tabId] = redirects[d.tabId] || {}; 112 | redirects[d.tabId][d.requestId] = true; 113 | } 114 | 115 | const r = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-allow-origin'); 116 | 117 | if (r) { 118 | if (r.value !== '*') { 119 | r.value = origin; 120 | } 121 | } 122 | else { 123 | responseHeaders.push({ 124 | 'name': 'Access-Control-Allow-Origin', 125 | 'value': origin 126 | }); 127 | } 128 | } 129 | } 130 | }); 131 | } 132 | 133 | // Referrer and Origin 134 | { 135 | v2.beforeSendHeaders.methods.add({ 136 | method: d => { 137 | if (v2.prefs['fix-origin']) { 138 | try { 139 | const o = new URL(d.url); 140 | d.requestHeaders.push({ 141 | name: 'referer', 142 | value: d.url 143 | }, { 144 | name: 'origin', 145 | value: o.origin 146 | }); 147 | } 148 | catch (e) {} 149 | } 150 | } 151 | }); 152 | } 153 | -------------------------------------------------------------------------------- /v2.5/worker.js: -------------------------------------------------------------------------------- 1 | /* global v2 */ 2 | self.DEFAULT_METHODS = [ 3 | 'GET', 'PUT', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK' 4 | ]; 5 | self.DEFAULT_STATUS_METHODS = ['GET', 'POST', 'PUT', 'OPTIONS', 'PATCH', 'PROPFIND', 'PROPPATCH']; 6 | 7 | const notify = e => alert(e.message || e); 8 | 9 | const core = {}; 10 | core.badge = () => chrome.storage.local.get({ 11 | enabled: false 12 | }, prefs => { 13 | chrome.browserAction.setIcon({ 14 | path: { 15 | '16': '/data/icons' + (prefs.enabled ? '' : '/disabled') + '/16.png', 16 | '32': '/data/icons' + (prefs.enabled ? '' : '/disabled') + '/32.png', 17 | '48': '/data/icons' + (prefs.enabled ? '' : '/disabled') + '/48.png' 18 | } 19 | }); 20 | chrome.browserAction.setTitle({ 21 | title: prefs.enabled ? 'Access-Control-Allow-Origin is unblocked' : 'Disabled: Default server behavior' 22 | }); 23 | }); 24 | 25 | core['overwrite-origin'] = () => chrome.storage.local.get({ 26 | 'enabled': false, 27 | 'overwrite-origin': true, 28 | 'fake-supported-methods': true, 29 | 'methods': self.DEFAULT_METHODS, 30 | 'unblock-initiator': true, // used in v2.install 31 | 'allow-credentials': true, // used in v2.install 32 | 'fix-origin': false // used in v2.install 33 | }, prefs => { 34 | if (prefs.enabled && (prefs['overwrite-origin'] || prefs['fix-origin'])) { 35 | v2.install(prefs); 36 | } 37 | else { 38 | v2.remove(); 39 | } 40 | 41 | if (prefs.enabled && prefs['overwrite-origin']) { 42 | const rules = { 43 | removeRuleIds: [1, 2], 44 | addRules: [{ 45 | 'id': 1, 46 | 'priority': 1, 47 | 'action': { 48 | 'type': 'modifyHeaders', 49 | 'responseHeaders': [{ 50 | 'operation': 'set', 51 | 'header': 'Access-Control-Allow-Methods', 52 | 'value': prefs.methods.length === self.DEFAULT_METHODS.length ? '*' : prefs.methods.join(', ') 53 | }] 54 | }, 55 | 'condition': {} 56 | }] 57 | }; 58 | if (prefs['fake-supported-methods']) { 59 | rules.addRules.push({ 60 | 'id': 2, 61 | 'priority': 1, 62 | 'action': { 63 | 'type': 'modifyHeaders', 64 | 'responseHeaders': [{ 65 | 'operation': 'set', 66 | 'header': 'Allow', 67 | 'value': prefs.methods.join(', ') 68 | }] 69 | }, 70 | 'condition': { 71 | 'requestMethods': ['options'] 72 | } 73 | }); 74 | } 75 | 76 | chrome.declarativeNetRequest.updateDynamicRules(rules); 77 | } 78 | else { 79 | chrome.declarativeNetRequest.updateDynamicRules({ 80 | removeRuleIds: [1, 2] 81 | }); 82 | } 83 | }); 84 | 85 | const toggle = (name, rule, value) => chrome.storage.local.get({ 86 | 'enabled': false, 87 | [name]: value 88 | }, prefs => { 89 | chrome.declarativeNetRequest.updateEnabledRulesets(prefs.enabled && prefs[name] ? { 90 | enableRulesetIds: [rule] 91 | } : { 92 | disableRulesetIds: [rule] 93 | }); 94 | }); 95 | 96 | core['csp'] = () => toggle('remove-csp', 'csp', false); 97 | core['allow-shared-array-buffer'] = () => toggle('allow-shared-array-buffer', 'allow-shared-array-buffer', false); 98 | core['x-frame'] = () => toggle('remove-x-frame', 'x-frame', true); 99 | core['allow-credentials'] = () => toggle('allow-credentials', 'allow-credentials', true); 100 | core['allow-headers'] = () => toggle('allow-headers', 'allow-headers', false); 101 | core['referer'] = () => toggle('remove-referer', 'referer', false); 102 | 103 | // changes 104 | { 105 | const once = () => { 106 | core.badge(); 107 | core['x-frame'](); 108 | core['overwrite-origin'](); 109 | core['allow-credentials'](); 110 | core['allow-headers'](); 111 | core['referer'](); 112 | core['csp'](); 113 | core['allow-shared-array-buffer'](); 114 | }; 115 | chrome.runtime.onStartup.addListener(once); 116 | chrome.runtime.onInstalled.addListener(once); 117 | } 118 | chrome.storage.onChanged.addListener(prefs => { 119 | if (prefs.enabled) { 120 | core.badge(); 121 | } 122 | if (prefs.enabled || prefs['remove-x-frame']) { 123 | core['x-frame'](); 124 | } 125 | if ( 126 | prefs.enabled || prefs['overwrite-origin'] || prefs.methods || 127 | prefs['allow-credentials'] || prefs['unblock-initiator'] || 128 | prefs['fix-origin'] || 129 | prefs['fake-supported-methods'] 130 | ) { 131 | core['overwrite-origin'](); 132 | } 133 | if (prefs.enabled || prefs['allow-credentials']) { 134 | core['allow-credentials'](); 135 | } 136 | if (prefs.enabled || prefs['allow-headers']) { 137 | core['allow-headers'](); 138 | } 139 | if (prefs.enabled || prefs['remove-referer']) { 140 | core['referer'](); 141 | } 142 | if (prefs.enabled || prefs['remove-csp']) { 143 | core['csp'](); 144 | } 145 | if (prefs.enabled || prefs['allow-shared-array-buffer']) { 146 | core['allow-shared-array-buffer'](); 147 | } 148 | 149 | // validate 150 | if (prefs['allow-credentials'] || prefs['unblock-initiator']) { 151 | chrome.storage.local.get({ 152 | 'allow-credentials': true, 153 | 'unblock-initiator': true 154 | }, prefs => { 155 | if (prefs['allow-credentials'] && prefs['unblock-initiator'] === false) { 156 | notify(`CORS Unblock Extension 157 | 158 | Conflicting Options: 159 | The value of the 'Access-Control-Allow-Origin' header must not be '*' when the credentials mode is 'include' 160 | 161 | How to Fix: 162 | Either disable sending credentials or enable allow origin only for the request initiator`); 163 | } 164 | }); 165 | } 166 | }); 167 | 168 | // action 169 | chrome.browserAction.onClicked.addListener(() => chrome.storage.local.get({ 170 | enabled: false 171 | }, prefs => chrome.storage.local.set({ 172 | enabled: prefs.enabled === false 173 | }))); 174 | 175 | /* FAQs & Feedback */ 176 | { 177 | const {management, runtime: {onInstalled, setUninstallURL, getManifest}, storage, tabs} = chrome; 178 | if (navigator.webdriver !== true) { 179 | const page = getManifest().homepage_url; 180 | const {name, version} = getManifest(); 181 | onInstalled.addListener(({reason, previousVersion}) => { 182 | management.getSelf(({installType}) => installType === 'normal' && storage.local.get({ 183 | 'faqs': true, 184 | 'last-update': 0 185 | }, prefs => { 186 | if (reason === 'install' || (prefs.faqs && reason === 'update')) { 187 | const doUpdate = (Date.now() - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45; 188 | if (doUpdate && previousVersion !== version) { 189 | tabs.query({active: true, currentWindow: true}, tbs => tabs.create({ 190 | url: page + '?version=' + version + (previousVersion ? '&p=' + previousVersion : '') + '&type=' + reason, 191 | active: reason === 'install', 192 | ...(tbs && tbs.length && {index: tbs[0].index + 1}) 193 | })); 194 | storage.local.set({'last-update': Date.now()}); 195 | } 196 | } 197 | })); 198 | }); 199 | setUninstallURL(page + '?rd=feedback&name=' + encodeURIComponent(name) + '&version=' + version); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /v2/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Tests: 4 | // https://mail.google.com/mail/u/0/#inbox 5 | // https://drive.google.com/drive/my-drive 6 | 7 | 8 | const DEFAULT_METHODS = ['GET', 'PUT', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK']; 9 | 10 | const prefs = { 11 | 'enabled': false, 12 | 'overwrite-origin': true, 13 | 'methods': DEFAULT_METHODS, 14 | 'remove-x-frame': true, 15 | 'allow-credentials': true, 16 | 'allow-headers-value': '*', 17 | 'allow-origin-value': '*', 18 | 'expose-headers-value': '*', 19 | 'allow-headers': false, 20 | 'unblock-initiator': true 21 | }; 22 | 23 | const redirects = {}; 24 | chrome.tabs.onRemoved.addListener(tabId => delete redirects[tabId]); 25 | const cors = {}; 26 | 27 | cors.onBeforeRedirect = d => { 28 | if (d.type === 'main_frame') { 29 | return; 30 | } 31 | redirects[d.tabId] = redirects[d.tabId] || {}; 32 | redirects[d.tabId][d.requestId] = true; 33 | }; 34 | 35 | cors.onHeadersReceived = d => { 36 | if (d.type === 'main_frame') { 37 | return; 38 | } 39 | const {initiator, originUrl, responseHeaders, requestId, tabId} = d; 40 | let origin = ''; 41 | 42 | const redirect = redirects[tabId] ? redirects[tabId][requestId] : false; 43 | if (prefs['unblock-initiator'] && redirect !== true) { 44 | try { 45 | const o = new URL(initiator || originUrl); 46 | origin = o.origin; 47 | } 48 | catch (e) { 49 | console.warn('cannot extract origin for initiator', initiator); 50 | } 51 | } 52 | else { 53 | origin = '*'; 54 | } 55 | if (redirects[tabId]) { 56 | delete redirects[tabId][requestId]; 57 | } 58 | 59 | if (prefs['overwrite-origin'] === true) { 60 | const o = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-allow-origin'); 61 | 62 | if (o) { 63 | if (o.value !== '*') { 64 | o.value = origin || prefs['allow-origin-value']; 65 | } 66 | } 67 | else { 68 | responseHeaders.push({ 69 | 'name': 'Access-Control-Allow-Origin', 70 | 'value': origin || prefs['allow-origin-value'] 71 | }); 72 | } 73 | } 74 | if (prefs.methods.length > 3) { // GET, POST, HEAD are mandatory 75 | const o = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-allow-methods'); 76 | if (o) { 77 | // only append methods that are not in the supported list 78 | o.value = [...new Set([...prefs.methods, ...o.value.split(/\s*,\s*/).filter(a => { 79 | return DEFAULT_METHODS.indexOf(a) === -1; 80 | })])].join(', '); 81 | } 82 | else { 83 | responseHeaders.push({ 84 | 'name': 'Access-Control-Allow-Methods', 85 | 'value': prefs.methods.join(', ') 86 | }); 87 | } 88 | } 89 | // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' 90 | // when the request's credentials mode is 'include'. 91 | if (prefs['allow-credentials'] === true) { 92 | const o = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-allow-origin'); 93 | if (!o || o.value !== '*') { 94 | const o = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-allow-credentials'); 95 | if (o) { 96 | o.value = 'true'; 97 | } 98 | else { 99 | responseHeaders.push({ 100 | 'name': 'Access-Control-Allow-Credentials', 101 | 'value': 'true' 102 | }); 103 | } 104 | } 105 | } 106 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers 107 | if (prefs['allow-headers'] === true) { 108 | const o = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-allow-headers'); 109 | if (o) { 110 | o.value = prefs['allow-headers-value']; 111 | } 112 | else { 113 | responseHeaders.push({ 114 | 'name': 'Access-Control-Allow-Headers', 115 | 'value': prefs['allow-headers-value'] 116 | }); 117 | } 118 | } 119 | if (prefs['allow-headers'] === true) { 120 | const o = responseHeaders.find(({name}) => name.toLowerCase() === 'access-control-expose-headers'); 121 | if (!o) { 122 | responseHeaders.push({ 123 | 'name': 'Access-Control-Expose-Headers', 124 | 'value': prefs['expose-headers-value'] 125 | }); 126 | } 127 | } 128 | if (prefs['remove-x-frame'] === true) { 129 | const i = responseHeaders.findIndex(({name}) => name.toLowerCase() === 'x-frame-options'); 130 | if (i !== -1) { 131 | responseHeaders.splice(i, 1); 132 | } 133 | } 134 | return {responseHeaders}; 135 | }; 136 | cors.install = () => { 137 | cors.remove(); 138 | const extra = ['blocking', 'responseHeaders']; 139 | if (/Firefox/.test(navigator.userAgent) === false) { 140 | extra.push('extraHeaders'); 141 | } 142 | chrome.webRequest.onHeadersReceived.addListener(cors.onHeadersReceived, { 143 | urls: [''] 144 | }, extra); 145 | chrome.webRequest.onBeforeRedirect.addListener(cors.onBeforeRedirect, { 146 | urls: [''] 147 | }); 148 | }; 149 | cors.remove = () => { 150 | chrome.webRequest.onHeadersReceived.removeListener(cors.onHeadersReceived); 151 | chrome.webRequest.onBeforeRedirect.removeListener(cors.onBeforeRedirect); 152 | }; 153 | 154 | cors.onCommand = () => { 155 | if (prefs.enabled) { 156 | cors.install(); 157 | } 158 | else { 159 | cors.remove(); 160 | } 161 | chrome.browserAction.setIcon({ 162 | path: { 163 | '16': 'data/icons/' + (prefs.enabled ? '' : 'disabled/') + '16.png', 164 | '19': 'data/icons/' + (prefs.enabled ? '' : 'disabled/') + '19.png', 165 | '32': 'data/icons/' + (prefs.enabled ? '' : 'disabled/') + '32.png', 166 | '38': 'data/icons/' + (prefs.enabled ? '' : 'disabled/') + '38.png', 167 | '48': 'data/icons/' + (prefs.enabled ? '' : 'disabled/') + '48.png', 168 | '64': 'data/icons/' + (prefs.enabled ? '' : 'disabled/') + '64.png' 169 | } 170 | }); 171 | chrome.browserAction.setTitle({ 172 | title: prefs.enabled ? 'Access-Control-Allow-Origin is unblocked' : 'Disabled: Default server behavior' 173 | }); 174 | }; 175 | 176 | chrome.storage.onChanged.addListener(ps => { 177 | Object.keys(ps).forEach(name => prefs[name] = ps[name].newValue); 178 | cors.onCommand(); 179 | }); 180 | 181 | chrome.browserAction.onClicked.addListener(() => chrome.storage.local.set({ 182 | enabled: prefs.enabled === false 183 | })); 184 | 185 | chrome.contextMenus.onClicked.addListener(({menuItemId, checked}) => { 186 | const ps = {}; 187 | if (menuItemId === 'test-cors') { 188 | chrome.tabs.create({ 189 | url: 'https://webbrowsertools.com/test-cors/' 190 | }); 191 | } 192 | else if (menuItemId === 'tutorial') { 193 | chrome.tabs.create({ 194 | url: 'https://www.youtube.com/watch?v=8berLeTjKDM' 195 | }); 196 | } 197 | else if (['overwrite-origin', 'remove-x-frame', 'allow-credentials', 'allow-headers', 'unblock-initiator'].indexOf(menuItemId) !== -1) { 198 | ps[menuItemId] = checked; 199 | } 200 | else { 201 | if (checked) { 202 | prefs.methods.push(menuItemId); 203 | } 204 | else { 205 | const index = prefs.methods.indexOf(menuItemId); 206 | if (index !== -1) { 207 | prefs.methods.splice(index, 1); 208 | } 209 | } 210 | ps.methods = prefs.methods; 211 | } 212 | 213 | chrome.storage.local.set(ps, () => chrome.storage.local.get({ 214 | 'allow-credentials': true, 215 | 'unblock-initiator': true 216 | }, prefs => { 217 | if (prefs['allow-credentials'] && prefs['unblock-initiator'] === false) { 218 | alert(`CORS Unblock Extension 219 | 220 | Conflicting Options: 221 | The value of the 'Access-Control-Allow-Origin' header must not be '*' when the credentials mode is 'include' 222 | 223 | How to Fix: 224 | Either disable sending credentials or enable allow origin only for the request initiator`); 225 | } 226 | })); 227 | }); 228 | 229 | /* init */ 230 | chrome.storage.local.get(prefs, ps => { 231 | Object.assign(prefs, ps); 232 | /* context menu */ 233 | chrome.contextMenus.create({ 234 | title: 'Test CORS', 235 | id: 'test-cors', 236 | contexts: ['browser_action'] 237 | }); 238 | chrome.contextMenus.create({ 239 | title: 'Usage Instruction', 240 | id: 'tutorial', 241 | contexts: ['browser_action'] 242 | }); 243 | chrome.contextMenus.create({ 244 | title: 'Enable Access-Control-Allow-Origin', 245 | type: 'checkbox', 246 | id: 'overwrite-origin', 247 | contexts: ['browser_action'], 248 | checked: prefs['overwrite-origin'] 249 | }); 250 | 251 | chrome.contextMenus.create({ 252 | title: 'Enable Access-Control-Allow-Credentials', 253 | type: 'checkbox', 254 | id: 'allow-credentials', 255 | contexts: ['browser_action'], 256 | checked: prefs['allow-credentials'] 257 | }); 258 | 259 | chrome.contextMenus.create({ 260 | title: 'Enable Access-Control-[Allow/Expose]-Headers', 261 | type: 'checkbox', 262 | id: 'allow-headers', 263 | contexts: ['browser_action'], 264 | checked: prefs['allow-headers'] 265 | }); 266 | 267 | const extra = chrome.contextMenus.create({ 268 | title: 'Extra Options', 269 | contexts: ['browser_action'] 270 | }); 271 | 272 | chrome.contextMenus.create({ 273 | title: 'Remove X-Frame-Options', 274 | type: 'checkbox', 275 | id: 'remove-x-frame', 276 | contexts: ['browser_action'], 277 | checked: prefs['remove-x-frame'], 278 | parentId: extra 279 | }); 280 | chrome.contextMenus.create({ 281 | title: 'Only Unblock Initiator', 282 | type: 'checkbox', 283 | id: 'unblock-initiator', 284 | contexts: ['browser_action'], 285 | checked: prefs['unblock-initiator'], 286 | parentId: extra 287 | }); 288 | 289 | 290 | const menu = chrome.contextMenus.create({ 291 | title: 'Access-Control-Allow-Methods Methods:', 292 | contexts: ['browser_action'], 293 | parentId: extra 294 | }); 295 | for (const method of ['PUT', 'DELETE', 'OPTIONS', 'PATCH', 'PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK']) { 296 | chrome.contextMenus.create({ 297 | title: method, 298 | type: 'checkbox', 299 | id: method, 300 | contexts: ['browser_action'], 301 | checked: prefs.methods.indexOf(method) !== -1, 302 | parentId: menu 303 | }); 304 | } 305 | 306 | cors.onCommand(); 307 | }); 308 | 309 | /* FAQs & Feedback */ 310 | { 311 | const {management, runtime: {onInstalled, setUninstallURL, getManifest}, storage, tabs} = chrome; 312 | if (navigator.webdriver !== true) { 313 | const page = getManifest().homepage_url; 314 | const {name, version} = getManifest(); 315 | onInstalled.addListener(({reason, previousVersion}) => { 316 | management.getSelf(({installType}) => installType === 'normal' && storage.local.get({ 317 | 'faqs': true, 318 | 'last-update': 0 319 | }, prefs => { 320 | if (reason === 'install' || (prefs.faqs && reason === 'update')) { 321 | const doUpdate = (Date.now() - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45; 322 | if (doUpdate && previousVersion !== version) { 323 | tabs.query({active: true, currentWindow: true}, tbs => tabs.create({ 324 | url: page + '?version=' + version + (previousVersion ? '&p=' + previousVersion : '') + '&type=' + reason, 325 | active: reason === 'install', 326 | ...(tbs && tbs.length && {index: tbs[0].index + 1}) 327 | })); 328 | storage.local.set({'last-update': Date.now()}); 329 | } 330 | } 331 | })); 332 | }); 333 | setUninstallURL(page + '?rd=feedback&name=' + encodeURIComponent(name) + '&version=' + version); 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /v2/data/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/128.png -------------------------------------------------------------------------------- /v2/data/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/16.png -------------------------------------------------------------------------------- /v2/data/icons/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/19.png -------------------------------------------------------------------------------- /v2/data/icons/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/256.png -------------------------------------------------------------------------------- /v2/data/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/32.png -------------------------------------------------------------------------------- /v2/data/icons/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/38.png -------------------------------------------------------------------------------- /v2/data/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/48.png -------------------------------------------------------------------------------- /v2/data/icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/512.png -------------------------------------------------------------------------------- /v2/data/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/64.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/128.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/16.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/19.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/256.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/32.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/38.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/48.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/512.png -------------------------------------------------------------------------------- /v2/data/icons/disabled/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v2/data/icons/disabled/64.png -------------------------------------------------------------------------------- /v2/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "version": "0.1.9", 4 | "name": "CORS Unblock", 5 | "description": "No more CORS error by appending 'Access-Control-Allow-Origin: *' header to local and remote web requests when enabled", 6 | "permissions": [ 7 | "storage", 8 | "", 9 | "webRequest", 10 | "webRequestBlocking", 11 | "contextMenus" 12 | ], 13 | "icons": { 14 | "16": "data/icons/16.png", 15 | "19": "data/icons/19.png", 16 | "32": "data/icons/32.png", 17 | "38": "data/icons/38.png", 18 | "48": "data/icons/48.png", 19 | "64": "data/icons/64.png", 20 | "128": "data/icons/128.png", 21 | "256": "data/icons/256.png", 22 | "512": "data/icons/512.png" 23 | }, 24 | "homepage_url": "https://add0n.com/access-control.html", 25 | "browser_action":{}, 26 | "background": { 27 | "scripts": [ 28 | "background.js" 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /v3/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": { 3 | "message": "No more CORS error by appending 'Access-Control-Allow-Origin: *' header to local and remote web requests when enabled" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /v3/data/debug/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 14px; 3 | font-family: system-ui, -apple-system, 'Segoe UI', Roboto, Ubuntu, Cantarell, 'Noto Sans', sans-serif; 4 | background-color: #fff; 5 | color: #4d5156; 6 | display: grid; 7 | height: 100vh; 8 | margin: 0; 9 | } 10 | select, 11 | button, 12 | input[type=submit], 13 | input[type=button] { 14 | height: 28px; 15 | color: #444; 16 | background-image: linear-gradient(rgb(237, 237, 237), rgb(237, 237, 237) 38%, rgb(222, 222, 222)); 17 | box-shadow: rgba(0, 0, 0, 0.08) 0 1px 0, rgba(255, 255, 255, 0.75) 0 1px 2px inset; 18 | text-shadow: rgb(240, 240, 240) 0 1px 0; 19 | cursor: pointer; 20 | } 21 | select, 22 | button, 23 | textarea, 24 | input { 25 | border: solid 1px rgba(0, 0, 0, 0.25); 26 | } 27 | input[type=button]:disabled { 28 | opacity: 0.5; 29 | } 30 | textarea { 31 | width: 100%; 32 | box-sizing: border-box; 33 | display: block; 34 | } 35 | textarea, 36 | input[type=text], 37 | input[type=number] { 38 | padding: 5px; 39 | outline: none; 40 | } 41 | textarea:focus, 42 | input[type=text]:focus, 43 | input[type=number]:focus { 44 | background-color: #e5f8ff; 45 | } 46 | a, 47 | a:visited { 48 | color: #07c; 49 | } 50 | table { 51 | width: 100%; 52 | } 53 | 54 | #methods { 55 | label { 56 | white-space: nowrap; 57 | } 58 | } 59 | #filters { 60 | display: grid; 61 | grid-template-columns: min-content 1fr; 62 | white-space: nowrap; 63 | grid-gap: 5px; 64 | align-items: center; 65 | } 66 | #forms { 67 | background-color: #e4e4e4; 68 | display: flex; 69 | flex-direction: column; 70 | gap: 10px; 71 | padding: 10px; 72 | margin-block-end: 20px; 73 | } 74 | #tools { 75 | height: fit-content; 76 | align-self: end; 77 | padding: 10px; 78 | display: grid; 79 | gap: 5px; 80 | margin-block-start: 20px; 81 | 82 | & > div { 83 | display: flex; 84 | flex-flow: nowrap; 85 | gap: 5px; 86 | overflow: hidden; 87 | } 88 | } 89 | #tip { 90 | display: grid; 91 | grid-template-columns: 1fr min-content; 92 | gap: 10px; 93 | place-content: center; 94 | background-color: #faffc7; 95 | padding-inline: 10px; 96 | 97 | & > span { 98 | display: flex; 99 | align-items: center; 100 | } 101 | } 102 | #start, 103 | #terminate { 104 | font-weight: bold; 105 | } 106 | #toast { 107 | align-self: center; 108 | text-overflow: ellipsis; 109 | overflow: hidden; 110 | white-space: nowrap; 111 | } 112 | .hidden { 113 | display: none !important; 114 | } 115 | .ftm { 116 | display: grid !important; 117 | grid-template-columns: 1fr repeat(3, min-content); 118 | } 119 | -------------------------------------------------------------------------------- /v3/data/debug/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 |
15 |
16 |
17 |

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

32 |
33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 | 46 | 54 | 55 | 56 | 62 | 66 | 67 | 68 | 74 | 75 | 76 | 82 | 83 | 84 | 90 | 91 | 92 | 98 | 99 | 100 | 106 | 107 | 108 | 114 | 115 | 116 | 122 | 123 | 124 | 125 | 126 | 127 | 133 | 134 | 135 | 141 | 142 | 143 | 149 | 150 | 151 | 157 | 158 | 159 | 165 | 169 | 170 | 171 | 177 | 181 | 182 |
41 | 45 | 47 | 48 | 49 | 53 |
57 | 61 | 63 | 64 | 65 |
69 | 73 |
77 | 81 |
85 | 89 |
93 | 97 |
101 | 105 |
109 | 113 |
117 | 121 |
[Requires Debugger]
128 | 132 |
136 | 140 |
144 | 148 |
152 | 156 |
160 | 164 | 166 | 167 | 168 |
172 | 176 | 178 | 179 | 180 |
183 |
184 |
185 |
186 | Preferences for This Hostname 187 | 188 | 189 | 190 | Preferences for All Hostnames 191 | 192 | 193 | 194 |
195 | 196 |
197 | 198 |
199 |
200 | 201 | 202 | 203 | 204 | 205 |
206 |
207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /v3/data/debug/index.js: -------------------------------------------------------------------------------- 1 | const DEFALUT_PREFS = { 2 | 'COPY': false, 3 | 'DELETE': true, 4 | 'GET': true, 5 | 'HEAD': true, 6 | 'LOCK': false, 7 | 'MKCOL': false, 8 | 'MOVE': false, 9 | 'OPTIONS': true, 10 | 'PATCH': true, 11 | 'POST': true, 12 | 'PROPFIND': false, 13 | 'PROPPATCH': false, 14 | 'PUT': true, 15 | 'acam-1': false, 16 | 'acam-2': true, 17 | 'acao-1': false, 18 | 'acao-2': true, 19 | 'acao-4': false, 20 | 'access-control-allow-credentials-enabled': true, 21 | 'access-control-allow-headers-enabled': false, 22 | 'access-control-allow-methods-checkbox': true, 23 | 'access-control-allow-origin-checkbox': true, 24 | 'access-control-expose-headers-enabled': false, 25 | 'access-control-request-headers-enabled': false, 26 | 'content-security-policy-enabled': false, 27 | 'fake-supported-methods': true, 28 | 'fix-redirect-checkbox': false, 29 | 'fix-origin': false, 30 | 'overwrite-4xx-checkbox': false, 31 | 'overwrite-501-checkbox': false, 32 | 'shared-array-buffer-enabled': true, 33 | 'x-frame-options-enabled': true, 34 | 'add-origin': false, 35 | 'aor-1': false, 36 | 'aor-2': true, 37 | 'add-referer': false, 38 | 'arr-1': false, 39 | 'arr-2': true 40 | }; 41 | 42 | const args = new URLSearchParams(location.search); 43 | const tabId = Number(args.get('id')); 44 | chrome.runtime.sendMessage({ 45 | method: 'terminate-if', 46 | tabId 47 | }).catch(e => {}); 48 | const START = Number(args.get('start')); 49 | let o; 50 | try { 51 | o = new URL(args.get('href')); 52 | } 53 | catch (e) { 54 | o = { 55 | hostname: '*', 56 | origin: '*' 57 | }; 58 | } 59 | 60 | const ttl = status => { 61 | if (status) { 62 | document.title = `[${status}] ` + 'CORS Unblock :: ' + args.get('title'); 63 | } 64 | else { 65 | document.title = 'CORS Unblock :: ' + args.get('title'); 66 | } 67 | }; 68 | ttl(); 69 | 70 | const toast = document.getElementById('toast'); 71 | const notify = msg => { 72 | clearTimeout(notify.id); 73 | toast.textContent = msg; 74 | notify.id = setTimeout(() => toast.textContent = '', 2000); 75 | }; 76 | 77 | // Access-Control-Allow-Origin 78 | // document.querySelector('input[data-id="acao-1"]').value = o.origin; 79 | document.querySelector('span[data-id="acao-3"]').textContent = o.origin; 80 | 81 | const update = async reason => { 82 | ttl('Running'); 83 | onbeforeunload = e => { 84 | e.preventDefault(); // Some browsers require this 85 | e.returnValue = ''; // Required for most browsers to show a prompt 86 | return ''; // For older browsers 87 | }; 88 | 89 | await chrome.debugger.detach({tabId}).catch(() => {}); 90 | 91 | const rules = []; 92 | // Access-Control-Allow-Origin 93 | if (document.getElementById('access-control-allow-origin-checkbox').checked) { 94 | let value = document.querySelector('input[name="access-control-allow-origin-radio"]:checked').value; 95 | if (value === '[origin]') { 96 | value = o.origin; 97 | } 98 | else if (value === '[custom]') { 99 | value = document.querySelector('input[data-id="acao-5"]').value; 100 | } 101 | rules.push({ 102 | id: START + 1, 103 | action: { 104 | type: 'modifyHeaders', 105 | responseHeaders: [{ 106 | header: 'Access-Control-Allow-Origin', 107 | operation: 'set', 108 | value 109 | }] 110 | }, 111 | condition: { 112 | resourceTypes: ['xmlhttprequest'], 113 | tabIds: [tabId] 114 | } 115 | }); 116 | } 117 | if (document.getElementById('access-control-allow-methods-checkbox').checked) { 118 | let value = document.querySelector('input[name="access-control-allow-methods-radio"]:checked').value; 119 | if (value === 'defined') { 120 | const methods = []; 121 | const f = new FormData(document.getElementById('methods')); 122 | for (const method of f.keys()) { 123 | methods.push(method); 124 | } 125 | value = methods.join(', '); 126 | } 127 | rules.push({ 128 | id: START + 2, 129 | action: { 130 | type: 'modifyHeaders', 131 | responseHeaders: [{ 132 | header: 'Access-Control-Allow-Methods', 133 | operation: 'set', 134 | value 135 | }] 136 | }, 137 | condition: { 138 | resourceTypes: ['xmlhttprequest'], 139 | tabIds: [tabId] 140 | } 141 | }); 142 | } 143 | if (document.getElementById('access-control-allow-credentials-enabled').checked) { 144 | rules.push({ 145 | id: START + 3, 146 | action: { 147 | type: 'modifyHeaders', 148 | responseHeaders: [{ 149 | header: 'Access-Control-Allow-Credentials', 150 | operation: 'set', 151 | value: 'true' 152 | }] 153 | }, 154 | condition: { 155 | resourceTypes: ['xmlhttprequest'], 156 | tabIds: [tabId] 157 | } 158 | }); 159 | } 160 | if (document.getElementById('access-control-allow-headers-enabled').checked) { 161 | rules.push({ 162 | id: START + 4, 163 | action: { 164 | type: 'modifyHeaders', 165 | responseHeaders: [{ 166 | header: 'Access-Control-Allow-Headers', 167 | operation: 'set', 168 | value: '*' 169 | }] 170 | }, 171 | condition: { 172 | excludedRequestMethods: ['options'], 173 | tabIds: [tabId] 174 | } 175 | }); 176 | } 177 | if (document.getElementById('access-control-expose-headers-enabled').checked) { 178 | rules.push({ 179 | id: START + 5, 180 | action: { 181 | type: 'modifyHeaders', 182 | responseHeaders: [{ 183 | header: 'Access-Control-Expose-Headers', 184 | operation: 'set', 185 | value: '*' 186 | }] 187 | }, 188 | condition: { 189 | excludedRequestMethods: ['options'], 190 | tabIds: [tabId] 191 | } 192 | }); 193 | } 194 | if (document.getElementById('x-frame-options-enabled').checked) { 195 | rules.push({ 196 | id: START + 6, 197 | action: { 198 | type: 'modifyHeaders', 199 | responseHeaders: [{ 200 | header: 'x-frame-options', 201 | operation: 'remove' 202 | }] 203 | }, 204 | condition: { 205 | resourceTypes: ['main_frame', 'sub_frame'], 206 | tabIds: [tabId] 207 | } 208 | }); 209 | } 210 | if (document.getElementById('content-security-policy-enabled').checked) { 211 | rules.push({ 212 | id: START + 7, 213 | action: { 214 | type: 'modifyHeaders', 215 | responseHeaders: [{ 216 | header: 'content-security-policy', 217 | operation: 'remove' 218 | }, { 219 | header: 'content-security-policy-report-only', 220 | operation: 'remove' 221 | }, { 222 | header: 'x-webkit-csp', 223 | operation: 'remove' 224 | }, { 225 | header: 'x-content-security-policy', 226 | operation: 'remove' 227 | }] 228 | }, 229 | condition: { 230 | resourceTypes: ['main_frame'], 231 | tabIds: [tabId] 232 | } 233 | }); 234 | } 235 | if (document.getElementById('shared-array-buffer-enabled').checked) { 236 | rules.push({ 237 | id: START + 8, 238 | action: { 239 | type: 'modifyHeaders', 240 | responseHeaders: [{ 241 | header: 'Cross-Origin-Opener-Policy', 242 | operation: 'set', 243 | value: 'same-origin' 244 | }, { 245 | header: 'Cross-Origin-Embedder-Policy', 246 | operation: 'set', 247 | value: 'require-corp' 248 | }] 249 | }, 250 | condition: { 251 | resourceTypes: ['main_frame', 'script', 'image'], 252 | tabIds: [tabId] 253 | } 254 | }, { 255 | id: START + 9, 256 | action: { 257 | type: 'modifyHeaders', 258 | responseHeaders: [{ 259 | header: 'Cross-Origin-Resource-Policy', 260 | operation: 'set', 261 | value: 'cross-origin' 262 | }] 263 | }, 264 | condition: { 265 | tabIds: [tabId] 266 | } 267 | }); 268 | } 269 | if (document.getElementById('fake-supported-methods').checked) { 270 | const methods = []; 271 | const f = new FormData(document.getElementById('methods')); 272 | for (const method of f.keys()) { 273 | methods.push(method); 274 | } 275 | rules.push({ 276 | 'id': START + 10, 277 | 'action': { 278 | 'type': 'modifyHeaders', 279 | 'responseHeaders': [{ 280 | 'operation': 'set', 281 | 'header': 'Allow', 282 | 'value': methods.join(', ') 283 | }] 284 | }, 285 | 'condition': { 286 | 'requestMethods': ['options'] 287 | } 288 | }); 289 | } 290 | // limit scope 291 | const scope = document.querySelector('input[name="urlFilter"]').value; 292 | if (scope) { 293 | for (const rule of rules) { 294 | if (rule.condition.resourceTypes) { 295 | if (rule.condition.resourceTypes.includes('main_frame') || rule.condition.resourceTypes.includes('sub_frame')) { 296 | continue; 297 | } 298 | } 299 | rule.condition.urlFilter = scope; 300 | } 301 | } 302 | console.info('[network rules]', rules); 303 | 304 | chrome.declarativeNetRequest.updateSessionRules({ 305 | addRules: rules, 306 | removeRuleIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(n => START + n) 307 | }).catch(e => { 308 | console.error(e); 309 | alert('Network modification failed:\n\n--\n' + e.message); 310 | }); 311 | 312 | // Status code 313 | if ( 314 | document.getElementById('overwrite-4xx-checkbox').checked || 315 | document.getElementById('overwrite-501-checkbox').checked || 316 | document.getElementById('access-control-request-headers-enabled').checked || 317 | document.getElementById('fix-redirect-checkbox').checked || 318 | document.getElementById('add-origin').checked || 319 | document.getElementById('add-referer').checked 320 | ) { 321 | const patterns = []; 322 | if ( 323 | document.getElementById('overwrite-4xx-checkbox').checked || 324 | document.getElementById('overwrite-501-checkbox').checked || 325 | document.getElementById('access-control-request-headers-enabled').checked || 326 | document.getElementById('fix-redirect-checkbox').checked 327 | ) { 328 | patterns.push({requestStage: 'Response'}); 329 | } 330 | if ( 331 | document.getElementById('add-origin').checked || 332 | document.getElementById('add-referer').checked 333 | ) { 334 | patterns.push({requestStage: 'Request'}); 335 | } 336 | 337 | await chrome.debugger.attach({tabId}, '1.3'); 338 | await chrome.debugger.sendCommand({tabId}, 'Fetch.enable', { 339 | patterns 340 | }); 341 | } 342 | }; 343 | 344 | const observe = {}; 345 | observe.response = (source, params) => { 346 | const opts = { 347 | requestId: params.requestId, 348 | responseHeaders: params.responseHeaders || [] 349 | }; 350 | let overwrite = false; 351 | 352 | if (document.getElementById('access-control-request-headers-enabled').checked) { 353 | if (params.request?.method === 'OPTIONS') { 354 | if ('Access-Control-Request-Headers' in params.request.headers) { 355 | opts.responseHeaders.push({ 356 | name: 'Access-Control-Allow-Headers', 357 | value: params.request.headers['Access-Control-Request-Headers'] 358 | }); 359 | overwrite = true; 360 | } 361 | } 362 | } 363 | if (document.getElementById('overwrite-4xx-checkbox').checked) { 364 | if (params.responseStatusCode >= 400 && params.responseStatusCode < 500) { 365 | opts.responseCode = 200; 366 | overwrite = true; 367 | } 368 | } 369 | if (document.getElementById('overwrite-501-checkbox').checked) { 370 | if (params.responseStatusCode === 501) { 371 | opts.responseCode = 200; 372 | opts.body = btoa('Supported\n'); 373 | overwrite = true; 374 | } 375 | } 376 | 377 | if (document.getElementById('fix-redirect-checkbox').checked) { 378 | if ( 379 | params.responseStatusCode === 301 || 380 | params.responseStatusCode === 302 381 | ) { 382 | const loc = opts.responseHeaders.find(o => o.name === 'location'); 383 | const {origin} = new URL(loc.value); 384 | 385 | const r = opts.responseHeaders.find(o => o.name === 'access-control-allow-origin'); 386 | if (r) { 387 | if (r.value !== '*') { 388 | opts.responseCode = params.responseStatusCode; 389 | r.value = origin; 390 | overwrite = true; 391 | } 392 | } 393 | else { 394 | opts.responseCode = params.responseStatusCode; 395 | opts.responseHeaders.push({ 396 | 'name': 'Access-Control-Allow-Origin', 397 | 'value': origin 398 | }); 399 | overwrite = true; 400 | } 401 | } 402 | if (params.redirectedRequestId) { 403 | const origin = '*'; 404 | const r = opts.responseHeaders.find(o => o.name === 'access-control-allow-origin'); 405 | if (r) { 406 | opts.responseCode = params.responseStatusCode; 407 | r.value = origin; 408 | overwrite = true; 409 | } 410 | else { 411 | opts.responseCode = params.responseStatusCode; 412 | opts.responseHeaders.push({ 413 | 'name': 'Access-Control-Allow-Origin', 414 | 'value': origin 415 | }); 416 | overwrite = true; 417 | } 418 | } 419 | } 420 | 421 | if (overwrite) { 422 | chrome.debugger.sendCommand(source, 'Fetch.fulfillRequest', opts); 423 | } 424 | else { 425 | chrome.debugger.sendCommand(source, 'Fetch.continueRequest', { 426 | requestId: params.requestId 427 | }); 428 | } 429 | }; 430 | observe.request = (source, params) => { 431 | const opts = { 432 | requestId: params.requestId 433 | }; 434 | const headers = new Map(); 435 | if (document.getElementById('add-origin').checked) { 436 | const type = document.querySelector('[name="add-origin-radio"]:checked').value; 437 | try { 438 | if (type === 'same') { 439 | const v = new URL(params.request.url); 440 | headers.set('origin', v.origin); 441 | } 442 | else { 443 | headers.set('origin', o.origin); 444 | } 445 | } 446 | catch (e) {} 447 | } 448 | if (document.getElementById('add-referer').checked) { 449 | const type = document.querySelector('[name="add-referer-radio"]:checked').value; 450 | try { 451 | if (type === 'same') { 452 | headers.set('referer', params.request.url); 453 | } 454 | else if (type === 'page') { 455 | headers.set('referer', o.href); 456 | } 457 | } 458 | catch (e) {} 459 | } 460 | if (headers.size) { 461 | opts.headers = Object.entries(params.request.headers).map(([name, value]) => ({ 462 | name, 463 | value 464 | })); 465 | for (const [name, value] of headers) { 466 | opts.headers.push({name, value}); 467 | } 468 | } 469 | chrome.debugger.sendCommand(source, 'Fetch.continueRequest', opts); 470 | }; 471 | 472 | chrome.debugger.onEvent.addListener((source, method, params) => { 473 | if (source.tabId !== tabId) { 474 | return; 475 | } 476 | if (method === 'Fetch.requestPaused') { 477 | if (params.responseHeaders) { 478 | observe.response(source, params); 479 | } 480 | else { 481 | observe.request(source, params); 482 | } 483 | } 484 | }); 485 | 486 | // update 487 | const load = prefs => { 488 | for (const input of document.querySelectorAll('#rules input')) { 489 | const value = prefs[input.dataset.id || input.name || input.id]; 490 | if (input.type === 'radio' || input.type === 'checkbox') { 491 | input.checked = value || false; 492 | } 493 | else { 494 | input.value = value || ''; 495 | } 496 | } 497 | }; 498 | chrome.storage.local.get(null, ps => { 499 | if (('prefs-' + o.hostname) in ps) { 500 | load(ps['prefs-' + o.hostname]); 501 | } 502 | else if ('prefs-*' in ps) { 503 | load(ps['prefs-*']); 504 | } 505 | else { 506 | load(DEFALUT_PREFS); 507 | } 508 | }); 509 | document.getElementById('start').onclick = e => { 510 | update(e.target.value); 511 | notify(`CORS unblocking is ${e.target.value.toLowerCase()}ed for this tab`); 512 | e.target.value = 'Restart'; 513 | chrome.action.setIcon({ 514 | tabId, 515 | path: { 516 | '16': '/data/icons/16.png', 517 | '32': '/data/icons/32.png', 518 | '48': '/data/icons/48.png' 519 | } 520 | }); 521 | }; 522 | 523 | // save 524 | document.getElementById('save-hostname').onclick = async () => { 525 | const prefs = {}; 526 | for (const input of document.querySelectorAll('#rules input')) { 527 | const value = input.type === 'radio' || input.type === 'checkbox' ? input.checked : input.value; 528 | prefs[input.dataset.id || input.name || input.id] = value; 529 | } 530 | await chrome.storage.local.set({ 531 | ['prefs-' + o.hostname]: prefs 532 | }); 533 | notify('Rules for this hostname are Saved'); 534 | }; 535 | document.getElementById('save-all').onclick = async () => { 536 | const prefs = {}; 537 | for (const input of document.querySelectorAll('#rules input')) { 538 | const value = input.type === 'radio' || input.type === 'checkbox' ? input.checked : input.value; 539 | prefs[input.dataset.id || input.name || input.id] = value; 540 | } 541 | await chrome.storage.local.set({ 542 | ['prefs-*']: prefs 543 | }); 544 | notify('Rules for all hostnames are Saved'); 545 | }; 546 | document.getElementById('load-all').onclick = () => chrome.storage.local.get({ 547 | 'prefs-*': DEFALUT_PREFS 548 | }, ps => { 549 | notify('Loading rules from storage for all hostnames'); 550 | load(ps['prefs-*']); 551 | }); 552 | document.getElementById('load-hostname').onclick = async () => { 553 | const prefs = await chrome.storage.local.get({ 554 | ['prefs-' + o.hostname]: DEFALUT_PREFS 555 | }); 556 | load(prefs['prefs-' + o.hostname]); 557 | notify('Loading rules from storage for this hostname'); 558 | }; 559 | 560 | document.getElementById('delete-hostname').onclick = async () => { 561 | await chrome.storage.local.remove('prefs-' + o.hostname); 562 | notify('Rules for this hostname are deleted'); 563 | }; 564 | document.getElementById('delete-all').onclick = async () => { 565 | await chrome.storage.local.remove('prefs-*'); 566 | notify('Reset to default.'); 567 | }; 568 | document.getElementById('terminate').onclick = async () => { 569 | await chrome.debugger.detach({tabId}).catch(() => {}); 570 | await chrome.declarativeNetRequest.updateSessionRules({ 571 | removeRuleIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(n => START + n) 572 | }); 573 | await chrome.action.setIcon({ 574 | tabId, 575 | path: { 576 | '16': '/data/icons/disabled/16.png', 577 | '32': '/data/icons/disabled/32.png', 578 | '48': '/data/icons/disabled/48.png' 579 | } 580 | }); 581 | onbeforeunload = null; 582 | window.close(); 583 | }; 584 | document.getElementById('terminate-all').onclick = async () => { 585 | const ids = (await chrome.declarativeNetRequest.getSessionRules()).map(o => o.id); 586 | await chrome.declarativeNetRequest.updateSessionRules({ 587 | removeRuleIds: ids 588 | }); 589 | chrome.runtime.sendMessage({ 590 | method: 'close-instance' 591 | }); 592 | document.getElementById('terminate').click(); 593 | }; 594 | document.getElementById('test').onclick = () => chrome.tabs.create({ 595 | url: 'https://webbrowsertools.com/test-cors/' 596 | }); 597 | document.getElementById('permission').onclick = e => chrome.permissions.request({ 598 | origins: [''] 599 | }, granted => { 600 | if (granted) { 601 | e.target.parentElement.classList.add('hidden'); 602 | } 603 | }); 604 | chrome.permissions.contains({ 605 | origins: [''] 606 | }, granted => { 607 | if (granted) { 608 | document.getElementById('permission').parentElement.classList.add('hidden'); 609 | } 610 | }); 611 | document.getElementById('close-tip').onclick = e => { 612 | e.target.closest('#tip').classList.add('hidden'); 613 | }; 614 | chrome.storage.local.get({ 615 | tip: true 616 | }, prefs => { 617 | if (prefs.tip) { 618 | document.getElementById('tip').classList.remove('hidden'); 619 | chrome.storage.local.set({ 620 | tip: false 621 | }); 622 | } 623 | }); 624 | 625 | chrome.runtime.onMessage.addListener(request => { 626 | if (request.method === 'close-instance') { 627 | document.getElementById('terminate').click(); 628 | } 629 | else if (request.method === 'terminate-if' && tabId === request.tabId) { 630 | document.getElementById('terminate').click(); 631 | } 632 | }); 633 | 634 | // links 635 | for (const a of [...document.querySelectorAll('[data-href]')]) { 636 | if (a.hasAttribute('href') === false) { 637 | a.href = chrome.runtime.getManifest().homepage_url + '#' + a.dataset.href; 638 | } 639 | } 640 | 641 | // Reactivate icon on reload 642 | chrome.tabs.onUpdated.addListener(id => { 643 | if (id === tabId) { 644 | if (onbeforeunload) { 645 | chrome.action.setIcon({ 646 | tabId, 647 | path: { 648 | '16': '/data/icons/16.png', 649 | '32': '/data/icons/32.png', 650 | '48': '/data/icons/48.png' 651 | } 652 | }); 653 | } 654 | } 655 | }); 656 | -------------------------------------------------------------------------------- /v3/data/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/128.png -------------------------------------------------------------------------------- /v3/data/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/16.png -------------------------------------------------------------------------------- /v3/data/icons/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/19.png -------------------------------------------------------------------------------- /v3/data/icons/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/256.png -------------------------------------------------------------------------------- /v3/data/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/32.png -------------------------------------------------------------------------------- /v3/data/icons/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/38.png -------------------------------------------------------------------------------- /v3/data/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/48.png -------------------------------------------------------------------------------- /v3/data/icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/512.png -------------------------------------------------------------------------------- /v3/data/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/64.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/128.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/16.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/19.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/256.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/32.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/38.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/48.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/512.png -------------------------------------------------------------------------------- /v3/data/icons/disabled/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balvin-perrie/Access-Control-Allow-Origin---Unblock/bfda150d179d81d7116135f42f1f5886b6491650/v3/data/icons/disabled/64.png -------------------------------------------------------------------------------- /v3/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "version": "0.5.2", 4 | "name": "CORS Unblock", 5 | "description": "__MSG_description__", 6 | "default_locale": "en", 7 | "permissions": [ 8 | "storage", 9 | "declarativeNetRequest", 10 | "debugger" 11 | ], 12 | "host_permissions": [ 13 | "" 14 | ], 15 | "icons": { 16 | "16": "/data/icons/disabled/16.png", 17 | "32": "/data/icons/disabled/32.png", 18 | "48": "/data/icons/disabled/48.png", 19 | "64": "/data/icons/disabled/64.png", 20 | "128": "/data/icons/disabled/128.png", 21 | "256": "/data/icons/disabled/256.png", 22 | "512": "/data/icons/disabled/512.png" 23 | }, 24 | "homepage_url": "https://webextension.org/listing/access-control.html", 25 | "action":{}, 26 | "background": { 27 | "service_worker": "worker.js" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /v3/worker.js: -------------------------------------------------------------------------------- 1 | chrome.action.onClicked.addListener(async tab => { 2 | const prefs = await chrome.storage.session.get({ 3 | start: 0 4 | }); 5 | 6 | const args = new URLSearchParams(); 7 | args.set('start', prefs.start); 8 | args.set('id', tab.id); 9 | args.set('href', tab.url); 10 | args.set('title', tab.title); 11 | const win = await chrome.windows.getCurrent(); 12 | 13 | chrome.windows.create({ 14 | url: '/data/debug/index.html?' + args.toString(), 15 | width: 800, 16 | height: 800, 17 | left: win.left + Math.round((win.width - 800) / 2), 18 | top: win.top + Math.round((win.height - 800) / 2), 19 | type: 'popup' 20 | }); 21 | 22 | chrome.storage.session.set({ 23 | start: prefs.start + 20 24 | }); 25 | }); 26 | 27 | /* FAQs & Feedback */ 28 | { 29 | const {management, runtime: {onInstalled, setUninstallURL, getManifest}, storage, tabs} = chrome; 30 | if (navigator.webdriver !== true) { 31 | const {homepage_url: page, name, version} = getManifest(); 32 | onInstalled.addListener(({reason, previousVersion}) => { 33 | management.getSelf(({installType}) => installType === 'normal' && storage.local.get({ 34 | 'faqs': true, 35 | 'last-update': 0 36 | }, prefs => { 37 | if (reason === 'install' || (prefs.faqs && reason === 'update')) { 38 | const doUpdate = (Date.now() - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45; 39 | if (doUpdate && previousVersion !== version) { 40 | tabs.query({active: true, lastFocusedWindow: true}, tbs => tabs.create({ 41 | url: page + '?version=' + version + (previousVersion ? '&p=' + previousVersion : '') + '&type=' + reason, 42 | active: reason === 'install', 43 | ...(tbs && tbs.length && {index: tbs[0].index + 1}) 44 | })); 45 | storage.local.set({'last-update': Date.now()}); 46 | } 47 | } 48 | })); 49 | }); 50 | setUninstallURL(page + '?rd=feedback&name=' + encodeURIComponent(name) + '&version=' + version); 51 | } 52 | } 53 | --------------------------------------------------------------------------------