diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-12-18 17:18:00 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-18 17:18:00 -0500 | 
| commit | 2fa9b915159d0bc43687e032d5f7f53197ef0611 (patch) | |
| tree | 9dfa01d93311704087493dcf458e126fe0f2288c | |
| parent | 05d4049f16715194842cf8cdf62345478288ee71 (diff) | |
Add support for using declarativeNetRequest (#1127)
| -rw-r--r-- | dev/data/manifest-variants.json | 4 | ||||
| -rw-r--r-- | ext/bg/js/backend.js | 1 | ||||
| -rw-r--r-- | ext/bg/js/request-builder.js | 133 | 
3 files changed, 137 insertions, 1 deletions
| diff --git a/dev/data/manifest-variants.json b/dev/data/manifest-variants.json index 75911771..64271327 100644 --- a/dev/data/manifest-variants.json +++ b/dev/data/manifest-variants.json @@ -154,8 +154,10 @@                  {"action": "move",   "path": ["content_security_policy_old"], "newPath": ["content_security_policy", "extension_pages"]},                  {"action": "move",   "path": ["sandbox", "content_security_policy"], "newPath": ["content_security_policy", "sandbox"]},                  {"action": "remove", "path": ["permissions"], "item": "<all_urls>"}, +                {"action": "remove", "path": ["permissions"], "item": "webRequest"}, +                {"action": "remove", "path": ["permissions"], "item": "webRequestBlocking"}, +                {"action": "add",    "path": ["permissions"], "items": ["declarativeNetRequest", "scripting"]},                  {"action": "set",    "path": ["host_permissions"], "value": ["<all_urls>"], "after": "optional_permissions"}, -                {"action": "add",    "path": ["permissions"], "items": ["scripting"]},                  {"action": "move",   "path": ["web_accessible_resources"], "newPath": ["web_accessible_resources_old"]},                  {"action": "set",    "path": ["web_accessible_resources"], "value": [{"resources": [], "matches": ["<all_urls>"]}], "after": "web_accessible_resources_old"},                  {"action": "move",   "path": ["web_accessible_resources_old"], "newPath": ["web_accessible_resources", 0, "resources"]} diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 322d9400..205c06b1 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -187,6 +187,7 @@ class Backend {              yomichan.on('log', this._onLog.bind(this)); +            await this._requestBuilder.prepare();              await this._environment.prepare();              this._clipboardReader.browser = this._environment.getInfo().browser; diff --git a/ext/bg/js/request-builder.js b/ext/bg/js/request-builder.js index e4380734..9c469056 100644 --- a/ext/bg/js/request-builder.js +++ b/ext/bg/js/request-builder.js @@ -19,9 +19,22 @@ class RequestBuilder {      constructor() {          this._extraHeadersSupported = null;          this._onBeforeSendHeadersExtraInfoSpec = ['blocking', 'requestHeaders', 'extraHeaders']; +        this._textEncoder = new TextEncoder(); +        this._ruleIds = new Set(); +    } + +    async prepare() { +        try { +            await this._clearDynamicRules(); +        } catch (e) { +            // NOP +        }      }      async fetchAnonymous(url, init) { +        if (isObject(chrome.declarativeNetRequest)) { +            return await this._fetchAnonymousDeclarative(url, init); +        }          const originURL = this._getOriginURL(url);          const modifications = [              ['cookie', null], @@ -130,4 +143,124 @@ class RequestBuilder {              }          }      } + +    async _clearDynamicRules() { +        if (!isObject(chrome.declarativeNetRequest)) { return; } + +        const rules = this._getDynamicRules(); + +        if (rules.length === 0) { return; } + +        const removeRuleIds = []; +        for (const {id} of rules) { +            removeRuleIds.push(id); +        } + +        await this._updateDynamicRules({removeRuleIds}); +    } + +    async _fetchAnonymousDeclarative(url, init) { +        const id = this._getNewRuleId(); +        const originUrl = this._getOriginURL(url); +        url = encodeURI(decodeURI(url)); + +        this._ruleIds.add(id); +        try { +            const addRules = [{ +                id, +                priority: 1, +                condition: { +                    urlFilter: `|${this._escapeDnrUrl(url)}|`, +                    resourceTypes: ['xmlhttprequest'] +                }, +                action: { +                    type: 'modifyHeaders', +                    requestHeaders: [ +                        { +                            operation: 'remove', +                            header: 'Cookie' +                        }, +                        { +                            operation: 'set', +                            header: 'Origin', +                            value: originUrl +                        } +                    ], +                    responseHeaders: [ +                        { +                            operation: 'remove', +                            header: 'Set-Cookie' +                        } +                    ] +                } +            }]; + +            await this._updateDynamicRules({addRules}); +            try { +                return await fetch(url, init); +            } finally { +                await this._tryUpdateDynamicRules({removeRuleIds: [id]}); +            } +        } finally { +            this._ruleIds.delete(id); +        } +    } + +    _getDynamicRules() { +        return new Promise((resolve, reject) => { +            chrome.declarativeNetRequest.getDynamicRules((result) => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(result); +                } +            }); +        }); +    } + +    _updateDynamicRules(options) { +        return new Promise((resolve, reject) => { +            chrome.declarativeNetRequest.updateDynamicRules(options, () => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(); +                } +            }); +        }); +    } + +    async _tryUpdateDynamicRules(options) { +        try { +            await this._updateDynamicRules(options); +            return true; +        } catch (e) { +            return false; +        } +    } + +    _getNewRuleId() { +        let id = 1; +        while (this._ruleIds.has(id)) { +            const pre = id; +            ++id; +            if (id === pre) { throw new Error('Could not generate an id'); } +        } +        return id; +    } + +    _escapeDnrUrl(url) { +        return url.replace(/[|*^]/g, (char) => this._urlEncodeUtf8(char)); +    } + +    _urlEncodeUtf8(text) { +        const array = this._textEncoder.encode(text); +        let result = ''; +        for (const byte of array) { +            result += `%${byte.toString(16).toUpperCase().padStart(2, '0')}`; +        } +        return result; +    }  } |