Best effort to run webRequest.onHeaderReceived listener last (issue #6)

This commit is contained in:
hackademix 2018-08-06 15:59:47 +02:00
parent 2250d51aa4
commit 0d8a94b008
2 changed files with 74 additions and 6 deletions

View File

@ -369,6 +369,14 @@ var RequestGuard = (() => {
onHeadersReceived(request) { onHeadersReceived(request) {
// called for main_frame, sub_frame and object // called for main_frame, sub_frame and object
// check for duplicate calls
let pending = pendingRequests.get(request.requestId);
if (pending && pending.headersProcessed) {
debug("[WARNING] already processed ", request);
}
pending.headersProcessed = true;
debug("onHeadersReceived", request); debug("onHeadersReceived", request);
let {url, documentUrl, statusCode, tabId, responseHeaders} = request; let {url, documentUrl, statusCode, tabId, responseHeaders} = request;
@ -478,7 +486,12 @@ var RequestGuard = (() => {
debug("%s scriptBlocked=%s setting noscriptFrame on ", request.url, scriptBlocked, request.tabId, request.frameId); debug("%s scriptBlocked=%s setting noscriptFrame on ", request.url, scriptBlocked, request.tabId, request.frameId);
TabStatus.record(request, "noscriptFrame", scriptBlocked); TabStatus.record(request, "noscriptFrame", scriptBlocked);
let pending = pendingRequests.get(request.requestId); let pending = pendingRequests.get(request.requestId);
if (pending) pending.scriptBlocked = scriptBlocked; if (pending) {
pending.scriptBlocked = scriptBlocked;
if (!pending.headersProcessed) {
debug("[WARNING] onHeadersReceived could not process", request);
}
}
}, },
onCompleted(request) { onCompleted(request) {
@ -533,12 +546,13 @@ var RequestGuard = (() => {
} }
return ABORT; return ABORT;
} }
const RequestGuard = { const RequestGuard = {
async start() { async start() {
let wr = browser.webRequest; let wr = browser.webRequest;
let listen = (what, ...args) => wr[what].addListener(listeners[what], ...args); let listen = (what, ...args) => wr[what].addListener(listeners[what], ...args);
let listenLast = (what, ...args) => new LastListener(wr[what], listeners[what], ...args).install();
let allUrls = ["<all_urls>"]; let allUrls = ["<all_urls>"];
let docTypes = ["main_frame", "sub_frame", "object"]; let docTypes = ["main_frame", "sub_frame", "object"];
@ -546,7 +560,7 @@ var RequestGuard = (() => {
{urls: allUrls, types: allTypes}, {urls: allUrls, types: allTypes},
["blocking"] ["blocking"]
); );
listen("onHeadersReceived", listenLast("onHeadersReceived",
{urls: allUrls, types: docTypes}, {urls: allUrls, types: docTypes},
["blocking", "responseHeaders"] ["blocking", "responseHeaders"]
); );
@ -570,8 +584,12 @@ var RequestGuard = (() => {
stop() { stop() {
let wr = browser.webRequest; let wr = browser.webRequest;
for (let [name, listener] of Object.entries(this.listeners)) { for (let [name, listener] of Object.entries(listeners)) {
wr[name].removeListener(listener); if (typeof listener === "function") {
wr[name].removeListener(listener);
} else if (listener instanceof LastListener) {
listener.uninstall();
}
} }
wr.onBeforeRequest.removeListener(onViolationReport); wr.onBeforeRequest.removeListener(onViolationReport);
} }

50
src/lib/LastListener.js Normal file
View File

@ -0,0 +1,50 @@
/**
* Wrapper around listeners on various WebExtensions
* APIs (e.g. webRequest.on*), as a best effort to
* let them run last by removing and re-adding them
* on each call (swapping 2 copies because
* addListener() calls are asynchronous).
* Note: we rely on implementation details Like
* listeners being called in addition order; also,
* clients should ensure they're not called twice for
* the same event, if that's important.
}
*/
class LastListener {
constructor(observed, listener, ...extras) {
this.observed = observed;
this.listener = listener;
this.extras = extras;
let ww = this._wrapped = [listener, listener].map(l => {
let w = (...args) => {
if (this.observed.hasListener(w._other)) {
this.observed.removeListener(w._other);
if (this.last === w) return this.defaultResult;
} else if (this.installed) {
this.observed.addListener(w._other, ...this.extras);
this.last = w._other;
}
return this.installed ? this.listener(...args)
: this.defaultResult;
}
return w;
});
ww[0]._other = ww[1];
ww[1]._other = ww[0];
this.installed = false;
this.defaultResult = null;
}
install() {
if (this.installed) return;
this.observed.addListener(this._wrapped[0], ...this.extras);
this.installed = true;
}
uninstall() {
this.installed = false;
for (let l of this._wrapped) this.observed.removeListener(l);
}
}