1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 09:37:12 +02:00

Firefox: new method for request handling

Now both nsIContentPolicy and on-http-* observers are used for net request
monitoring.

Reasons:
 - In many cases, nsIContentPolicy.shouldLoad is invoked twice for the same
   resource, because of the speculative parsing.
 - nsIContentPolicy.shouldLoad don't have information about the channel,
   so it can't redirect the request, nor change its headers, however
   on-http-opening-request can.

Also, local mirroring and inline-script blocking has been implemented.
This commit is contained in:
Deathamns 2014-12-24 23:11:36 +01:00
parent 720794357c
commit dbfacad8a6
4 changed files with 193 additions and 79 deletions

View File

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* global Services, Components, XPCOMUtils */ /* global Services, Components, XPCOMUtils, __URI__ */
'use strict'; 'use strict';
@ -36,11 +36,15 @@ Cu['import']('resource://gre/modules/XPCOMUtils.jsm');
/******************************************************************************/ /******************************************************************************/
const getMessager = win => const getMessageManager = function(context) {
win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell) return context
.sameTypeRootTreeItem.QueryInterface(Ci.nsIDocShell) .QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.sameTypeRootTreeItem
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor) .QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager); .getInterface(Ci.nsIContentFrameMessageManager);
};
/******************************************************************************/ /******************************************************************************/
@ -49,8 +53,7 @@ const contentPolicy = {
classID: Components.ID('{e6d173c8-8dbf-4189-a6fd-189e8acffd27}'), classID: Components.ID('{e6d173c8-8dbf-4189-a6fd-189e8acffd27}'),
contractID: '@' + appName + '/content-policy;1', contractID: '@' + appName + '/content-policy;1',
ACCEPT: Ci.nsIContentPolicy.ACCEPT, ACCEPT: Ci.nsIContentPolicy.ACCEPT,
REJECT: Ci.nsIContentPolicy.REJECT_REQUEST, messageName: appName + ':shouldLoad',
requestMessageName: appName + ':onBeforeRequest',
get componentRegistrar() { get componentRegistrar() {
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar); return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
}, },
@ -93,6 +96,7 @@ const contentPolicy = {
false false
); );
}, },
// https://bugzil.la/612921
shouldLoad: function(type, location, origin, context) { shouldLoad: function(type, location, origin, context) {
if (!context || !/^https?$/.test(location.scheme)) { if (!context || !/^https?$/.test(location.scheme)) {
return this.ACCEPT; return this.ACCEPT;
@ -102,23 +106,17 @@ const contentPolicy = {
? context.contentWindow || context ? context.contentWindow || context
: (context.ownerDocument || context).defaultView; : (context.ownerDocument || context).defaultView;
if (!win) { if (win) {
return this.ACCEPT; getMessageManager(win).sendSyncMessage(this.messageName, {
url: location.spec,
type: type,
frameId: type === 6 ? -1 : (win === win.top ? 0 : 1),
parentFrameId: win === win.top ? -1 : 0
});
} }
let result = getMessager(win).sendSyncMessage(this.requestMessageName, {
url: location.spec,
type: type,
tabId: -1, // determined in background script
frameId: type === 6 ? -1 : (win === win.top ? 0 : 1),
parentFrameId: win === win.top ? -1 : 0
})[0];
return result === true ? this.REJECT : this.ACCEPT;
}/*,
shouldProcess: function() {
return this.ACCEPT; return this.ACCEPT;
}*/ }
}; };
/******************************************************************************/ /******************************************************************************/
@ -126,7 +124,7 @@ const contentPolicy = {
const docObserver = { const docObserver = {
contentBaseURI: 'chrome://' + appName + '/content/', contentBaseURI: 'chrome://' + appName + '/content/',
initContext: function(win, sandbox) { initContext: function(win, sandbox) {
let messager = getMessager(win); let messager = getMessageManager(win);
if (sandbox) { if (sandbox) {
win = Cu.Sandbox([win], { win = Cu.Sandbox([win], {

View File

@ -23,20 +23,18 @@
/******************************************************************************/ /******************************************************************************/
// https://bugzil.la/673569
(function(frameScriptContext) { (function(frameScriptContext) {
'use strict'; 'use strict';
/******************************************************************************/ /******************************************************************************/
let appName; let appName = Components.stack.filename.match(/:\/\/([^\/]+)/)[1];
let listeners = {}; let listeners = {};
try { throw new Error; } catch (ex) { Components.utils['import'](Components.stack.filename.replace('Script', 'Module'), {});
appName = ex.fileName.match(/:\/\/([^\/]+)/)[1];
}
Components.utils['import']('chrome://' + appName + '/content/frameModule.js', {});
/******************************************************************************/ /******************************************************************************/

View File

@ -321,6 +321,7 @@ vAPI.tabs.registerListeners = function() {
// onClosed - handled in windowWatcher.onTabClose // onClosed - handled in windowWatcher.onTabClose
// onPopup ? // onPopup ?
for (var win of this.getWindows()) { for (var win of this.getWindows()) {
windowWatcher.onReady.call(win); windowWatcher.onReady.call(win);
} }
@ -600,10 +601,21 @@ vAPI.setIcon = function(tabId, img, badge) {
} }
var button = curWin.document.getElementById(vAPI.toolbarButton.widgetId); var button = curWin.document.getElementById(vAPI.toolbarButton.widgetId);
if (!button) {
return;
}
/*if (!button.classList.contains('badged-button')) {
button.classList.add('badged-button');
}*/
var icon = vAPI.tabIcons[tabId]; var icon = vAPI.tabIcons[tabId];
button.setAttribute('badge', icon && icon.badge || ''); button.setAttribute('badge', icon && icon.badge || '');
icon = vAPI.getURL(icon && icon.img || 'img/browsericons/icon16-off.svg'); button.image = vAPI.getURL(
button.style.listStyleImage = 'url(' + icon + ')'; button.image && icon && icon.img || 'img/browsericons/icon16-off.svg'
);
}; };
/******************************************************************************/ /******************************************************************************/
@ -623,11 +635,11 @@ vAPI.toolbarButton.init = function() {
defaultArea: CustomizableUI.AREA_NAVBAR, defaultArea: CustomizableUI.AREA_NAVBAR,
label: vAPI.app.name, label: vAPI.app.name,
tooltiptext: vAPI.app.name, tooltiptext: vAPI.app.name,
onViewShowing: function(e) { onViewShowing: function({target}) {
e.target.firstChild.setAttribute('src', vAPI.getURL('popup.html')); target.firstChild.setAttribute('src', vAPI.getURL('popup.html'));
}, },
onViewHiding: function(e) { onViewHiding: function({target}) {
e.target.firstChild.setAttribute('src', 'about:blank'); target.firstChild.setAttribute('src', 'about:blank');
} }
}); });
@ -645,11 +657,9 @@ vAPI.toolbarButton.register = function(doc) {
var panel = doc.createElement('panelview'); var panel = doc.createElement('panelview');
panel.setAttribute('id', this.panelId); panel.setAttribute('id', this.panelId);
var iframe = panel.appendChild(doc.createElement('iframe')); var iframe = doc.createElement('iframe');
iframe.setAttribute('type', 'content'); iframe.setAttribute('type', 'content');
panel.style.overflow = iframe.style.overflow = 'hidden';
doc.getElementById('PanelUI-multiView') doc.getElementById('PanelUI-multiView')
.appendChild(panel) .appendChild(panel)
.appendChild(iframe); .appendChild(iframe);
@ -672,14 +682,13 @@ vAPI.toolbarButton.register = function(doc) {
}; };
var onPopupReady = function() { var onPopupReady = function() {
if (!this.contentWindow var win = this.contentWindow;
|| this.contentWindow.location.host !== location.host) {
if (!win || win.location.host !== location.host) {
return; return;
} }
var mutObs = this.contentWindow.MutationObserver; new win.MutationObserver(delayedResize).observe(win.document, {
(new mutObs(delayedResize)).observe(this.contentDocument, {
childList: true, childList: true,
attributes: true, attributes: true,
characterData: true, characterData: true,
@ -712,7 +721,7 @@ vAPI.toolbarButton.register = function(doc) {
'#' + this.panelId + ', #' + this.panelId + ' > iframe {', '#' + this.panelId + ', #' + this.panelId + ' > iframe {',
'width: 180px;', 'width: 180px;',
'height: 310px;', 'height: 310px;',
'transition: width .1s, height .1s;', 'overflow: hidden !important;',
'}' '}'
].join('')); ].join(''));
@ -860,14 +869,127 @@ vAPI.messaging.broadcast = function(message) {
/******************************************************************************/ /******************************************************************************/
vAPI.net = { vAPI.net = {};
beforeRequestMessageName: location.host + ':onBeforeRequest'
/******************************************************************************/
var httpObserver = {
ABORT: Components.results.NS_BINDING_ABORTED,
lastRequest: {
url: null,
type: null,
tabId: null,
frameId: null,
parentFrameId: null
},
observe: function(httpChannel, topic) {
if (!(httpChannel instanceof Ci.nsIHttpChannel)) {
return;
}
httpChannel = httpChannel.QueryInterface(Ci.nsIHttpChannel);
var URI = httpChannel.URI, tabId, result;
// the first distinct character
topic = topic.charAt(8);
// http-on-modify-request
if (topic === 'm') {
// var onHeadersReceived = vAPI.net.onHeadersReceived;
return;
}
// http-on-examine-request
if (topic === 'e') {
try {
tabId = httpChannel.getProperty('tabId');
} catch (ex) {
return;
}
if (!tabId) {
return;
}
var header = 'Content-Security-Policy';
try {
result = httpChannel.getResponseHeader(header);
} catch (ex) {
result = null;
}
result = vAPI.net.onHeadersReceived.callback({
url: URI.spec,
tabId: tabId,
parentFrameId: -1,
responseHeaders: result ? [{name: header, value: result}] : []
});
if (result) {
httpChannel.setResponseHeader(
header,
result.responseHeaders[0].value,
true
);
}
return;
}
// http-on-opening-request
var lastRequest = this.lastRequest;
if (!lastRequest.url || lastRequest.url !== URI.spec) {
lastRequest.url = null;
return;
}
// Important! When loading file via XHR for mirroring,
// the URL will be the same, so it could fall into an infinite loop
lastRequest.url = null;
if (lastRequest.type === 'main_frame'
&& httpChannel instanceof Ci.nsIWritablePropertyBag) {
httpChannel.setProperty('tabId', lastRequest.tabId);
}
var onBeforeRequest = vAPI.net.onBeforeRequest;
if (!onBeforeRequest.types.has(lastRequest.type)) {
return;
}
result = onBeforeRequest.callback({
url: URI.spec,
type: lastRequest.type,
tabId: lastRequest.tabId,
frameId: lastRequest.frameId,
parentFrameId: lastRequest.parentFrameId
});
if (!result || typeof result !== 'object') {
return;
}
if (result.cancel === true) {
httpChannel.cancel(this.ABORT);
}
else if (result.redirectUrl) {
httpChannel.redirectionLimit = 1;
httpChannel.redirectTo(
Services.io.newURI(result.redirectUrl, null, null)
);
}
}
}; };
/******************************************************************************/ /******************************************************************************/
vAPI.net.registerListeners = function() { vAPI.net.registerListeners = function() {
var types = { var typeMap = {
2: 'script', 2: 'script',
3: 'image', 3: 'image',
4: 'stylesheet', 4: 'stylesheet',
@ -877,42 +999,36 @@ vAPI.net.registerListeners = function() {
11: 'xmlhttprequest' 11: 'xmlhttprequest'
}; };
var onBeforeRequest = this.onBeforeRequest; this.onBeforeRequest.types = new Set(this.onBeforeRequest.types);
this.onBeforeRequest = function(e) { var shouldLoadListenerMessageName = location.host + ':shouldLoad';
var details = e.data; var shouldLoadListener = function(e) {
var lastRequest = httpObserver.lastRequest;
details.type = types[details.type] || 'other'; lastRequest.url = e.data.url;
details.tabId = vAPI.tabs.getTabId(e.target); lastRequest.type = typeMap[e.data.type] || 'other';
lastRequest.tabId = vAPI.tabs.getTabId(e.target);
if (onBeforeRequest.types.indexOf(details.type) === -1) { lastRequest.frameId = e.data.frameId;
return false; lastRequest.parentFrameId = e.data.parentFrameId;
}
var block = onBeforeRequest.callback(details);
if (block && typeof block === 'object') {
if (block.cancel === true) {
return true;
}
else if (block.redirectURL) {
return block.redirectURL;
}
}
return false;
}; };
vAPI.messaging.globalMessageManager.addMessageListener( vAPI.messaging.globalMessageManager.addMessageListener(
this.beforeRequestMessageName, shouldLoadListenerMessageName,
this.onBeforeRequest shouldLoadListener
); );
Services.obs.addObserver(httpObserver, 'http-on-opening-request', false);
// Services.obs.addObserver(httpObserver, 'http-on-modify-request', false);
Services.obs.addObserver(httpObserver, 'http-on-examine-response', false);
vAPI.unload.push(function() { vAPI.unload.push(function() {
vAPI.messaging.globalMessageManager.removeMessageListener( vAPI.messaging.globalMessageManager.removeMessageListener(
vAPI.net.beforeRequestMessageName, shouldLoadListenerMessageName,
vAPI.net.onBeforeRequest shouldLoadListener
); );
Services.obs.removeObserver(httpObserver, 'http-on-opening-request');
// Services.obs.removeObserver(httpObserver, 'http-on-modify-request');
Services.obs.removeObserver(httpObserver, 'http-on-examine-response');
}); });
}; };

View File

@ -95,10 +95,10 @@ vAPI.messaging = {
this.channels['vAPI'] = {}; this.channels['vAPI'] = {};
this.channels['vAPI'].listener = function(msg) { this.channels['vAPI'].listener = function(msg) {
if (msg.cmd === 'injectScript') { if ( msg.cmd === 'injectScript' ) {
var details = msg.details; var details = msg.details;
if (!details.allFrames && window !== window.top) { if ( !details.allFrames && window !== window.top ) {
return; return;
} }
@ -108,12 +108,14 @@ vAPI.messaging = {
}, },
close: function() { close: function() {
if (this.connector) { if ( !this.connector ) {
removeMessageListener(this.connectorId, this.connector); return;
this.connector = null;
this.channels = {};
this.listeners = {};
} }
removeMessageListener(this.connectorId, this.connector);
this.connector = null;
this.channels = {};
this.listeners = {};
}, },
channel: function(channelName, callback) { channel: function(channelName, callback) {