code review: make onHeadersReceive() able to cancel responses

This commit is contained in:
gorhill 2015-11-09 17:59:19 -05:00
parent 064ef87f73
commit 3d472beb1b
2 changed files with 96 additions and 62 deletions

View file

@ -1749,9 +1749,6 @@ var httpObserver = {
ACCEPT: Components.results.NS_SUCCEEDED,
// Request types:
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy#Constants
MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT,
VALID_CSP_TARGETS: 1 << Ci.nsIContentPolicy.TYPE_DOCUMENT |
1 << Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
typeMap: {
1: 'other',
2: 'script',
@ -1769,6 +1766,10 @@ var httpObserver = {
19: 'beacon',
21: 'image'
},
onBeforeRequest: function(){},
onBeforeRequestTypes: null,
onHeadersReceived: function(){},
onHeadersReceivedTypes: null,
get componentRegistrar() {
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
@ -1935,14 +1936,12 @@ var httpObserver = {
},
handleRequest: function(channel, URI, details) {
var onBeforeRequest = vAPI.net.onBeforeRequest;
var type = this.typeMap[details.rawtype] || 'other';
if ( onBeforeRequest.types && onBeforeRequest.types.has(type) === false ) {
if ( this.onBeforeRequestTypes && this.onBeforeRequestTypes.has(type) === false ) {
return false;
}
var result = onBeforeRequest.callback({
var result = this.onBeforeRequest({
frameId: details.frameId,
hostname: URI.asciiHost,
parentFrameId: details.parentFrameId,
@ -1963,65 +1962,85 @@ var httpObserver = {
return false;
},
getResponseHeader: function(channel, name) {
var value;
try {
value = channel.getResponseHeader(name);
} catch (ex) {
}
return value;
},
handleResponseHeaders: function(channel, URI, channelData) {
var type = this.typeMap[channelData[4]] || 'other';
if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(type) === false ) {
return;
}
// 'Content-Security-Policy' MUST come last in the array. Need to
// revised this eventually.
var responseHeaders = [];
var value = this.getResponseHeader(channel, 'Content-Security-Policy');
if ( value !== undefined ) {
responseHeaders.push({ name: 'Content-Security-Policy', value: value });
}
var result = this.onHeadersReceived({
hostname: URI.asciiHost,
parentFrameId: channelData[1],
responseHeaders: responseHeaders,
tabId: channelData[3],
type: this.typeMap[channelData[4]] || 'other',
url: URI.asciiSpec
});
if ( !result ) {
return;
}
if ( result.cancel ) {
channel.cancel(this.ABORT);
return;
}
if ( result.responseHeaders ) {
channel.setResponseHeader(
'Content-Security-Policy',
result.responseHeaders.pop().value,
true
);
return;
}
},
observe: function(channel, topic) {
if ( channel instanceof Ci.nsIHttpChannel === false ) {
return;
}
var URI = channel.URI;
var channelData, result;
if ( topic === 'http-on-examine-response' ) {
if ( !(channel instanceof Ci.nsIWritablePropertyBag) ) {
if ( channel instanceof Ci.nsIWritablePropertyBag === false ) {
return;
}
var channelData;
try {
channelData = channel.getProperty(this.REQDATAKEY);
} catch (ex) {
return;
}
if ( !channelData ) {
return;
}
if ( (1 << channelData[4] & this.VALID_CSP_TARGETS) === 0 ) {
return;
}
topic = 'Content-Security-Policy';
try {
result = channel.getResponseHeader(topic);
} catch (ex) {
result = null;
}
result = vAPI.net.onHeadersReceived.callback({
hostname: URI.asciiHost,
parentFrameId: channelData[1],
responseHeaders: result ? [{name: topic, value: result}] : [],
tabId: channelData[3],
type: this.typeMap[channelData[4]] || 'other',
url: URI.asciiSpec
});
if ( result ) {
channel.setResponseHeader(
topic,
result.responseHeaders.pop().value,
true
);
}
this.handleResponseHeaders(channel, URI, channelData);
return;
}
// http-on-opening-request
//console.log('http-on-opening-request:', URI.spec);
var pendingRequest = this.lookupPendingRequest(URI.spec);
var rawtype = channel.loadInfo && channel.loadInfo.contentPolicyType || 1;
@ -2108,6 +2127,7 @@ var httpObserver = {
}
};
/******************************************************************************/
/******************************************************************************/
vAPI.net = {};
@ -2118,9 +2138,19 @@ vAPI.net.registerListeners = function() {
// Since it's not used
this.onBeforeSendHeaders = null;
this.onBeforeRequest.types = this.onBeforeRequest.types ?
new Set(this.onBeforeRequest.types) :
null;
if ( typeof this.onBeforeRequest.callback === 'function' ) {
httpObserver.onBeforeRequest = this.onBeforeRequest.callback;
httpObserver.onBeforeRequestTypes = this.onBeforeRequest.types ?
new Set(this.onBeforeRequest.types) :
null;
}
if ( typeof this.onHeadersReceived.callback === 'function' ) {
httpObserver.onHeadersReceived = this.onHeadersReceived.callback;
httpObserver.onHeadersReceivedTypes = this.onHeadersReceived.types ?
new Set(this.onHeadersReceived.types) :
null;
}
var shouldBlockPopup = function(details) {
var sourceTabId = null;

View file

@ -331,28 +331,33 @@ var onHeadersReceived = function(details) {
return;
}
// Special handling for root document.
if ( details.type === 'main_frame' ) {
var requestType = details.type;
if ( requestType === 'main_frame' ) {
return onRootFrameHeadersReceived(details);
}
// Just in case...
if ( details.type !== 'sub_frame' ) {
return;
if ( requestType === 'sub_frame' ) {
return onFrameHeadersReceived(details);
}
};
// If we reach this point, we are dealing with a sub_frame
/******************************************************************************/
var onRootFrameHeadersReceived = function(details) {
var µb = µBlock;
var tabId = details.tabId;
µb.tabContextManager.push(tabId, details.url);
// Lookup the page store associated with this tab id.
var µb = µBlock;
var pageStore = µb.pageStoreFromTabId(tabId);
if ( !pageStore ) {
return;
pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest');
}
// I can't think of how pageStore could be null at this point.
// Frame id of frame request is their own id, while the request is made
// in the context of the parent.
var context = pageStore.createContextFromFrameId(details.parentFrameId);
var context = pageStore.createContextFromPage();
context.requestURL = details.url;
context.requestHostname = details.hostname;
context.requestType = 'inline-script';
@ -385,20 +390,19 @@ var onHeadersReceived = function(details) {
/******************************************************************************/
var onRootFrameHeadersReceived = function(details) {
var tabId = details.tabId;
var onFrameHeadersReceived = function(details) {
var µb = µBlock;
µb.tabContextManager.push(tabId, details.url);
var tabId = details.tabId;
// Lookup the page store associated with this tab id.
var pageStore = µb.pageStoreFromTabId(tabId);
if ( !pageStore ) {
pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest');
return;
}
// I can't think of how pageStore could be null at this point.
var context = pageStore.createContextFromPage();
// Frame id of frame request is their own id, while the request is made
// in the context of the parent.
var context = pageStore.createContextFromFrameId(details.parentFrameId);
context.requestURL = details.url;
context.requestHostname = details.hostname;
context.requestType = 'inline-script';