code review for c5d85881181a: mind about:blank et al. iframes

This commit is contained in:
Raymond Hill 2018-05-20 06:49:12 -04:00
parent db67c6bcc4
commit 0c5e2eb7ee
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
2 changed files with 109 additions and 24 deletions

View File

@ -878,30 +878,28 @@ vAPI.domCollapser = (function() {
attributeFilter: [ 'src' ]
};
// The injected scriptlets are those which were injected in the current
// document, from within `bootstrapPhase1`, and which scriptlets are
// selectively looked-up from:
// https://github.com/uBlockOrigin/uAssets/blob/master/filters/resources.txt
var primeLocalIFrame = function(iframe) {
// Should probably also copy injected styles.
// The injected scripts are those which were injected in the current
// document, from within the `contentscript-start.js / injectScripts`,
// and which scripts are selectively looked-up from:
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
if ( vAPI.injectedScripts ) {
vAPI.injectScriptlet(iframe.contentDocument, vAPI.injectedScripts);
}
};
var addIFrame = function(iframe, dontObserve) {
// https://github.com/gorhill/uBlock/issues/162
// Be prepared to deal with possible change of src attribute.
var addIFrame = function(iframe, dontObserve) {
if ( dontObserve !== true ) {
iframeSourceObserver.observe(iframe, iframeSourceObserverOptions);
}
var src = iframe.src;
if ( src === '' || typeof src !== 'string' ) {
primeLocalIFrame(iframe);
return;
}
if ( src.lastIndexOf('http', 0) !== 0 ) { return; }
if ( src.startsWith('http') === false ) { return; }
toFilter[toFilter.length] = {
type: 'sub_frame',
url: iframe.src

View File

@ -36,12 +36,39 @@
scriptletsRegister = new Map(),
reEscapeScriptArg = /[\\'"]/g;
// Purpose of `contentscriptCode` below is too programmatically inject
// content script code which only purpose is to inject scriptlets. This
// essentially does the same as what uBO's declarative content script does,
// except that this allows to inject the scriptlets earlier than it is
// possible through the declarative content script.
//
// Declaratively:
// 1. Browser injects generic content script =>
// 2. Content script queries scriptlets =>
// 3. Main process sends scriptlets =>
// 4. Content script injects scriptlets
//
// Programmatically:
// 1. uBO injects specific scriptlets-aware content script =>
// 2. Content script injects scriptlets
//
// However currently this programmatic injection works well only on
// Chromium-based browsers, it does not work properly with Firefox. More
// investigations is needed to find out why this fails with Firefox.
// Consequently, the programmatic-injection code path is taken only with
// Chromium-based browsers.
let contentscriptCode = (function() {
let parts = [
'(',
function(hostname, scriptlets) {
if ( hostname !== window.location.hostname ) { return; }
let d = document;
if (
document.location === null ||
hostname !== document.location.hostname
) {
return;
}
let injectScriptlets = function(d) {
let script = d.createElement('script');
try {
script.appendChild(d.createTextNode(
@ -53,6 +80,66 @@
if ( script.parentNode ) {
script.parentNode.removeChild(script);
}
};
injectScriptlets(document);
let processIFrame = function(iframe) {
let src = iframe.src;
if ( /^https?:\/\//.test(src) === false ) {
injectScriptlets(iframe.contentDocument);
}
};
let observerTimer,
observerLists = [];
let observerAsync = function() {
for ( let nodelist of observerLists ) {
for ( let node of nodelist ) {
if ( node.nodeType !== 1 ) { continue; }
if ( node.parentElement === null ) { continue; }
if ( node.localName === 'iframe' ) {
processIFrame(node);
}
if ( node.childElementCount === 0 ) { continue; }
let iframes = node.querySelectorAll('iframe');
for ( let iframe of iframes ) {
processIFrame(iframe);
}
}
}
observerLists = [];
observerTimer = undefined;
};
let ready = function(ev) {
if ( ev !== undefined ) {
window.removeEventListener(ev.type, ready);
}
let iframes = document.getElementsByTagName('iframe');
if ( iframes.length !== 0 ) {
observerLists.push(iframes);
observerTimer = setTimeout(observerAsync, 1);
}
let observer = new MutationObserver(function(mutations) {
for ( let mutation of mutations ) {
if ( mutation.addedNodes.length !== 0 ) {
observerLists.push(mutation.addedNodes);
}
}
if (
observerLists.length !== 0 &&
observerTimer === undefined
) {
observerTimer = setTimeout(observerAsync, 1);
}
});
observer.observe(
document.documentElement,
{ childList: true, subtree: true }
);
};
if ( document.readyState === 'loading' ) {
window.addEventListener('DOMContentLoaded', ready);
} else {
ready();
}
}.toString(),
')(',
'"', 'hostname-slot', '", ',
@ -283,9 +370,6 @@
if ( out.length === 0 ) { return; }
if ( µb.hiddenSettings.debugScriptlets ) {
out.unshift('debugger;');
}
return out.join('\n');
};
@ -305,12 +389,15 @@
let scriptlets = µb.scriptletFilteringEngine.retrieve(request);
if ( scriptlets === undefined ) { return; }
let code = contentscriptCode.assemble(request.hostname, scriptlets);
if ( µb.hiddenSettings.debugScriptlets ) {
code = 'debugger;\n' + code;
}
chrome.tabs.executeScript(
details.tabId,
{
code: code,
frameId: details.frameId,
matchAboutBlank: true,
matchAboutBlank: false,
runAt: 'document_start'
}
);