aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-12-18 17:18:00 -0500
committerGitHub <noreply@github.com>2020-12-18 17:18:00 -0500
commit2fa9b915159d0bc43687e032d5f7f53197ef0611 (patch)
tree9dfa01d93311704087493dcf458e126fe0f2288c
parent05d4049f16715194842cf8cdf62345478288ee71 (diff)
Add support for using declarativeNetRequest (#1127)
-rw-r--r--dev/data/manifest-variants.json4
-rw-r--r--ext/bg/js/backend.js1
-rw-r--r--ext/bg/js/request-builder.js133
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;
+ }
}