1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-15 15:32:28 +02:00

local mirroring work

This commit is contained in:
gorhill 2014-10-02 16:45:26 -04:00
parent 50c169d22e
commit 52538d1e41
9 changed files with 356 additions and 130 deletions

View File

@ -93,7 +93,7 @@ return {
}, },
'assets/ublock/privacy.txt': { 'assets/ublock/privacy.txt': {
off: true, off: true,
title: 'µBlock filters - Privacy', title: 'µBlock filters Privacy',
group: 'default' group: 'default'
} }
}, },

View File

@ -529,29 +529,25 @@ var getPageDetails = function(µb, tabId) {
var µburi = µb.URI; var µburi = µb.URI;
var dict = pageStore.netFilteringCache.fetchAll(); var dict = pageStore.netFilteringCache.fetchAll();
var r = []; var r = [];
var details, pos, result, hostname, domain; var details, hostname, domain;
for ( var url in dict ) { for ( var url in dict ) {
if ( dict.hasOwnProperty(url) === false ) { if ( dict.hasOwnProperty(url) === false ) {
continue; continue;
} }
details = dict[url].data; details = dict[url];
if ( typeof details !== 'string' ) { if ( wantBlocked !== pageStore.boolFromResult(details.result) ) {
continue;
}
pos = details.indexOf('\t');
result = details.slice(pos + 1);
if ( wantBlocked !== pageStore.boolFromResult(result) ) {
continue; continue;
} }
hasher.appendStr(url); hasher.appendStr(url);
hasher.appendStr(details); hasher.appendStr(details.result);
hostname = µburi.hostnameFromURI(url); hostname = µburi.hostnameFromURI(url);
domain = µburi.domainFromHostname(hostname) || hostname; domain = µburi.domainFromHostname(hostname) || hostname;
r.push({ r.push({
type: details.slice(0, pos),
domain: domain,
url: url, url: url,
reason: result domain: domain,
reason: details.result,
type: details.type,
flags: details.flags
}); });
} }
return r; return r;

View File

@ -19,6 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* jshint bitwise: false */
/* global chrome, YaMD5, µBlock */ /* global chrome, YaMD5, µBlock */
/******************************************************************************/ /******************************************************************************/
@ -29,7 +30,16 @@
/******************************************************************************/ /******************************************************************************/
// To show keys in local storage from console:
// chrome.storage.local.get(null, function (data) { console.log(Object.keys(data)) });
// To cleanup cached items from console:
// chrome.storage.local.get(null, function (data) { chrome.storage.local.remove(Object.keys(data).filter(function(a){ return a.indexOf('mirrors_item_') === 0; })); });
var exports = { var exports = {
bytesInUseMax: 5 * 1024 * 1024,
ttl: 21 * 24 * 60 * 60 * 1000,
bytesInUse: 0, bytesInUse: 0,
hitCount: 0 hitCount: 0
}; };
@ -38,6 +48,8 @@ var exports = {
var nullFunc = function() {}; var nullFunc = function() {};
// TODO: need to come up with something better. Key shoud be domain. More
// control over what significant part(s) of a URL is to be used as key.
var mirrorCandidates = { var mirrorCandidates = {
'ajax.googleapis.com': /^ajax\.googleapis\.com\/ajax\/libs\//, 'ajax.googleapis.com': /^ajax\.googleapis\.com\/ajax\/libs\//,
'fonts.googleapis.com': /^fonts\.googleapis\.com/, 'fonts.googleapis.com': /^fonts\.googleapis\.com/,
@ -45,28 +57,35 @@ var mirrorCandidates = {
'cdnjs.cloudflare.com': /^cdnjs\.cloudflare\.com\/ajax\/libs\//, 'cdnjs.cloudflare.com': /^cdnjs\.cloudflare\.com\/ajax\/libs\//,
'code.jquery.com': /^code\.jquery\.com/, 'code.jquery.com': /^code\.jquery\.com/,
's0.2mdn.net': /(2mdn\.net\/instream\/html5\/ima3\.js)/, 's0.2mdn.net': /(2mdn\.net\/instream\/html5\/ima3\.js)/,
'connect.facebook.net': /(connect\.facebook\.net\/[^\/]+\/all\.js)/, 'www.googletagservices.com': /(www\.googletagservices\.com\/tag\/js\/gpt\.js)/,
'www.googletagservices.com': /(www\.googletagservices\.com\/tag\/js\/gpt\.js)/ 'maxcdn.bootstrapcdn.com': /^maxcdn\.bootstrapcdn\.com\/font-awesome\//,
'b.scorecardresearch.com': /^b\.scorecardresearch\.com\/beacon\.js/,
'platform.twitter.com': /^platform\.twitter\.com\/widgets\.js/,
'cdn.quilt.janrain.com': /^cdn\.quilt\.janrain\.com\//
}; };
var magicId = 'rmwwgwkzcgfv'; var magicId = 'rmwwgwkzcgfv';
var bytesInUseMax = 20 * 1024 * 1024;
var ttl = 30 * 24 * 60 * 60 * 1000;
var metadataPersistTimer = null; var metadataPersistTimer = null;
var bytesInUseMercy = 1 * 1024 * 1024;
var metadata = { var metadata = {
magicId: magicId, magicId: magicId,
urlKeyToHashMap: {} urlKeyToHashMap: {}
}; };
// Hash to content map var hashToContentMap = {};
var hashToDataUrlMap = {};
var loaded = false; var loaded = false;
/******************************************************************************/ /******************************************************************************/
// Ideally, URL keys and access time would be attached to the data URL entry
// itself, but then this would mean the need to persist the whole data URL
// every time a new URL key is added or the data URL is accessed, and given the
// data URL can be quite large, that would make no sense efficiency-wise to
// re-persist the whole thing.
// So, ContentEntry persisted once, MetadataEntry persisted often.
var MetadataEntry = function(hash) { var MetadataEntry = function(hash) {
this.accessTime = Date.now(); this.accessTime = Date.now();
this.hash = hash; this.hash = hash;
@ -98,6 +117,64 @@ var getTextFileFromURL = function(url, onLoad, onError) {
/******************************************************************************/ /******************************************************************************/
// Safe binary-to-base64. Because window.btoa doesn't work for binary data...
//
// This implementation doesn't require the creation of a full-length
// intermediate buffer. I expect less short-term memory use will translate in
// more efficient conversion. Hopefully I will get time to confirm with
// benchmarks in the future.
var btoaMap = (function(){
var out = new Uint8Array(64);
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var i = chars.length;
while ( i-- ) {
out[i] = chars.charCodeAt(i);
}
return out;
})();
var btoaSafe = function(input) {
var output = [];
var bamap = btoaMap;
var n = Math.floor(input.length / 3) * 3;
var b1, b2, b3;
for ( var ii = 0; ii < n; ii += 3 ) {
b1 = input.charCodeAt(ii );
b2 = input.charCodeAt(ii+1);
b3 = input.charCodeAt(ii+2);
output.push(String.fromCharCode(
bamap[ b1 >>> 2],
bamap[(b1 & 0x03) << 4 | b2 >>> 4],
bamap[(b2 & 0x0F) << 2 | b3 >>> 6],
bamap[ b3 & 0x3F ]
));
}
// Leftover
var m = input.length - n;
if ( m > 1 ) {
b1 = input.charCodeAt(ii );
b2 = input.charCodeAt(ii+1);
output.push(String.fromCharCode(
bamap[ b1 >>> 2],
bamap[(b1 & 0x03) << 4 | b2 >>> 4],
bamap[(b2 & 0x0F) << 2],
0x3D
));
} else if ( m !== 0 ) {
b1 = input.charCodeAt(ii);
output.push(String.fromCharCode(
bamap[ b1 >>>2],
bamap[(b1 & 0x03) << 4 ],
0x3D,
0x3D
));
}
return output.join('');
};
/******************************************************************************/
// Extract a `key` from a URL. // Extract a `key` from a URL.
var toUrlKey = function(url) { var toUrlKey = function(url) {
@ -111,10 +188,10 @@ var toUrlKey = function(url) {
url = url.slice(pos + 3); url = url.slice(pos + 3);
pos = url.indexOf('/'); pos = url.indexOf('/');
if ( pos === -1 ) { if ( pos === -1 ) {
return -1; return '';
} }
var re = mirrorCandidates[url.slice(0, pos)]; var re = mirrorCandidates[url.slice(0, pos)];
if ( typeof re !== 'object' || typeof re.test !== 'function' ) { if ( typeof re !== 'object' || typeof re.exec !== 'function' ) {
return ''; return '';
} }
var matches = re.exec(url); var matches = re.exec(url);
@ -148,13 +225,84 @@ var normalizeContentType = function(ctin) {
/******************************************************************************/ /******************************************************************************/
var metadataExists = function(urlKey) { var metadataExists = function(urlKey) {
return typeof urlKey === 'string' && metadata.urlKeyToHashMap.hasOwnProperty(urlKey); return typeof urlKey === 'string' &&
metadata.urlKeyToHashMap.hasOwnProperty(urlKey);
}; };
/******************************************************************************/ /******************************************************************************/
var contentExists = function(hash) { var contentExists = function(hash) {
return typeof hash === 'string' && hashToDataUrlMap.hasOwnProperty(hash); return typeof hash === 'string' &&
hashToContentMap.hasOwnProperty(hash);
};
/******************************************************************************/
var storageKeyFromHash = function(hash) {
return 'mirrors_item_' + hash;
};
/******************************************************************************/
// Given that a single data URL can be shared by many URL keys, pruning is a
// bit hairy. So the steps are:
// - Collate information about each data URL:
// - Last time they were used
// - Which URL keys reference them
// This will allow us to flush from memory the ones least recently used first.
var pruneToSize = function(toSize) {
if ( exports.bytesInUse < toSize ) {
return;
}
var k2hMap = metadata.urlKeyToHashMap;
var h2cMap = hashToContentMap;
var urlKey, hash;
var mdEntry, ctEntry, prEntry;
var pruneMap = {};
for ( urlKey in k2hMap ) {
if ( k2hMap.hasOwnProperty(urlKey) === false ) {
continue;
}
mdEntry = k2hMap[urlKey];
hash = mdEntry.hash;
if ( pruneMap.hasOwnProperty(hash) === false ) {
pruneMap[hash] = {
urlKeys: [urlKey],
accessTime: mdEntry.accessTime
};
continue;
}
prEntry = pruneMap[hash];
prEntry.urlKeys.push(urlKey);
prEntry.accessTime = Math.max(prEntry.accessTime, mdEntry.accessTime);
}
// Least recent at the end of array
var compare = function(a, b) {
return pruneMap[b].accessTime - pruneMap[a].accessTime;
};
var hashes = Object.keys(pruneMap).sort(compare);
var toRemove = [];
var i = hashes.length;
while ( i-- ) {
hash = hashes[i];
prEntry = pruneMap[hash];
ctEntry = h2cMap[hash];
delete h2cMap[hash];
toRemove.push(storageKeyFromHash(hash));
exports.bytesInUse -= ctEntry.dataURL.length;
while ( urlKey = prEntry.urlKeys.pop() ) {
delete k2hMap[urlKey];
}
if ( exports.bytesInUse < toSize ) {
break;
}
}
if ( toRemove.length !== 0 ) {
//console.debug('mirrors.pruneToSize(%d): removing %o', toSize, toRemove);
removeContent(toRemove);
updateMetadataNow();
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -166,16 +314,7 @@ var updateMetadata = function() {
/******************************************************************************/ /******************************************************************************/
var updateMetadataAsync = function(urlKey, hash) { var updateMetadataNow = function() {
var doesExist = metadataExists(urlKey);
if ( doesExist ) {
metadata.urlKeyToHashMap[urlKey].accessTime = Date.now();
if ( metadataPersistTimer === null ) {
setTimeout(updateMetadata, 60 * 1000);
}
return;
}
metadata.urlKeyToHashMap[urlKey] = new MetadataEntry(hash);
if ( metadataPersistTimer !== null ) { if ( metadataPersistTimer !== null ) {
clearTimeout(metadataPersistTimer); clearTimeout(metadataPersistTimer);
} }
@ -184,16 +323,45 @@ var updateMetadataAsync = function(urlKey, hash) {
/******************************************************************************/ /******************************************************************************/
var updateContent = function(hash, dataURL) { var updateMetadataAsync = function() {
if ( contentExists(hash) !== false ) { if ( metadataPersistTimer === null ) {
setTimeout(updateMetadata, 60 * 1000);
}
};
/******************************************************************************/
var addMetadata = function(urlKey, hash) {
metadata.urlKeyToHashMap[urlKey] = new MetadataEntry(hash);
updateMetadataNow();
};
/******************************************************************************/
var removeMetadata = function(urlKey) {
delete metadata.urlKeyToHashMap[urlKey];
};
/******************************************************************************/
var addContent = function(hash, dataURL) {
if ( contentExists(hash) ) {
return; return;
} }
var contentEntry = hashToDataUrlMap[hash] = new ContentEntry(dataURL); var contentEntry = hashToContentMap[hash] = new ContentEntry(dataURL);
exports.bytesInUse += dataURL.length; exports.bytesInUse += dataURL.length;
var key = 'mirrors_item_' + hash;
var bin = {}; var bin = {};
bin[key] = contentEntry; bin[storageKeyFromHash(hash)] = contentEntry;
chrome.storage.local.set(bin); chrome.storage.local.set(bin);
if ( exports.bytesInUse >= exports.bytesInUseMax + bytesInUseMercy ) {
pruneToSize(exports.bytesInUseMax);
}
};
/******************************************************************************/
var removeContent = function(what) {
chrome.storage.local.remove(what);
}; };
/******************************************************************************/ /******************************************************************************/
@ -218,7 +386,7 @@ var cacheAsset = function(url) {
yamd5.appendAsciiStr(contentType); yamd5.appendAsciiStr(contentType);
yamd5.appendAsciiStr(this.response); yamd5.appendAsciiStr(this.response);
var hash = yamd5.end(); var hash = yamd5.end();
updateMetadataAsync(urlKey, hash); addMetadata(urlKey, hash);
if ( contentExists(hash) ) { if ( contentExists(hash) ) {
//console.debug('mirrors.cacheAsset(): reusing existing content for "%s"', urlKey); //console.debug('mirrors.cacheAsset(): reusing existing content for "%s"', urlKey);
return; return;
@ -226,10 +394,18 @@ var cacheAsset = function(url) {
//console.debug('mirrors.cacheAsset(): caching new content for "%s"', urlKey); //console.debug('mirrors.cacheAsset(): caching new content for "%s"', urlKey);
// Keep original encoding if there was one, otherwise use base64 -- // Keep original encoding if there was one, otherwise use base64 --
// as the result is somewhat more compact I believe // as the result is somewhat more compact I believe
var dataUrl = contentType.indexOf(';') !== -1 ? var dataUrl = null;
'data:' + contentType + ',' + encodeURIComponent(this.responseText) : try {
'data:' + contentType + ';base64,' + btoa(this.response); dataUrl = contentType.indexOf(';') !== -1 ?
updateContent(hash, dataUrl); 'data:' + contentType + ',' + encodeURIComponent(this.responseText) :
'data:' + contentType + ';base64,' + btoa(this.response);
} catch (e) {
//console.debug(e);
}
if ( dataUrl === null ) {
dataUrl = 'data:' + contentType + ';base64,' + btoaSafe(this.response);
}
addContent(hash, dataUrl);
}; };
var onRemoteAssetError = function() { var onRemoteAssetError = function() {
@ -256,14 +432,18 @@ var toURL = function(url, cache) {
} }
return ''; return '';
} }
var dataURL = '';
var metadataEntry = metadata.urlKeyToHashMap[urlKey]; var metadataEntry = metadata.urlKeyToHashMap[urlKey];
if ( contentExists(metadataEntry.hash) === false ) { if ( contentExists(metadataEntry.hash) ) {
return ''; dataURL = hashToContentMap[metadataEntry.hash].dataURL;
metadataEntry.accessTime = Date.now();
exports.hitCount += 1;
} else {
//console.debug('mirrors.toURL(): content not found "%s"', url);
delete metadata.urlKeyToHashMap[urlKey];
} }
var contentEntry = hashToDataUrlMap[metadataEntry.hash]; updateMetadataAsync();
updateMetadataAsync(urlKey); return dataURL;
exports.hitCount += 1;
return contentEntry.dataURL;
}; };
/******************************************************************************/ /******************************************************************************/
@ -271,46 +451,55 @@ var toURL = function(url, cache) {
var load = function() { var load = function() {
loaded = true; loaded = true;
var loadContent = function(hash) { var loadContent = function(urlKey, hash) {
var key = 'mirrors_item_' + hash; var binKey = storageKeyFromHash(hash);
var onContentReady = function(bin) { var onContentReady = function(bin) {
if ( chrome.runtime.lastError ) { if ( chrome.runtime.lastError || bin.hasOwnProperty(binKey) === false ) {
//console.debug('mirrors.load(): failed to load content "%s"', binKey);
removeMetadata(urlKey);
removeContent(binKey);
return; return;
} }
var contentEntry = bin[key]; //console.debug('mirrors.load(): loaded content "%s"', binKey);
hashToDataUrlMap[hash] = contentEntry; var ctEntry = hashToContentMap[hash] = bin[binKey];
exports.bytesInUse += contentEntry.dataURL.length; exports.bytesInUse += ctEntry.dataURL.length;
}; };
var bin = {}; chrome.storage.local.get(binKey, onContentReady);
bin[key] = '';
chrome.storage.local.get(bin, onContentReady);
}; };
var onMetadataReady = function(bin) { var onMetadataReady = function(bin) {
if ( chrome.runtime.lastError ) { //console.debug('mirrors.load(): loaded metadata');
return;
}
metadata = bin.mirrors_metadata; metadata = bin.mirrors_metadata;
var hemap = metadata.urlKeyToHashMap; var toRemove = [];
for ( var urlKey in hemap ) { var u2hmap = metadata.urlKeyToHashMap;
if ( hemap.hasOwnProperty(urlKey) === false ) { var hash;
for ( var urlKey in u2hmap ) {
if ( u2hmap.hasOwnProperty(urlKey) === false ) {
continue; continue;
} }
loadContent(hemap[urlKey].hash); hash = u2hmap[urlKey].hash;
if ( metadata.magicId !== magicId ) {
toRemove.push(storageKeyFromHash(hash));
removeMetadata(urlKey);
continue;
}
loadContent(urlKey, hash);
}
if ( toRemove.length !== 0 ) {
removeContent(toRemove);
updateMetadataNow();
} }
}; };
chrome.storage.local.get({ 'mirrors_metadata' : metadata }, onMetadataReady); chrome.storage.local.get({ 'mirrors_metadata': metadata }, onMetadataReady);
}; };
/******************************************************************************/ /******************************************************************************/
var unload = function() { var unload = function() {
if ( metadataPersistTimer !== null ) { updateMetadataNow();
updateMetadata();
}
metadata.urlKeyToHashMap = {}; metadata.urlKeyToHashMap = {};
hashToDataUrlMap = {}; hashToContentMap = {};
exports.bytesInUse = 0; exports.bytesInUse = 0;
exports.hitCount = 0; exports.hitCount = 0;
@ -332,6 +521,7 @@ exports.toggle = function(on) {
// Export API // Export API
exports.toURL = toURL; exports.toURL = toURL;
exports.pruneToSize = pruneToSize;
return exports; return exports;

View File

@ -19,6 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* jshint bitwise: false */
/* global µBlock */ /* global µBlock */
/******************************************************************************* /*******************************************************************************
@ -48,21 +49,24 @@ var netFilteringResultCacheEntryJunkyardMax = 200;
/******************************************************************************/ /******************************************************************************/
var NetFilteringResultCacheEntry = function(data) { var NetFilteringResultCacheEntry = function(result, type, flags) {
this.init(data); this.init(result, type, flags);
}; };
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCacheEntry.prototype.init = function(data) { NetFilteringResultCacheEntry.prototype.init = function(result, type, flags) {
this.data = data; this.result = result;
this.type = type;
this.flags = flags;
this.time = Date.now(); this.time = Date.now();
}; };
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCacheEntry.prototype.dispose = function() { NetFilteringResultCacheEntry.prototype.dispose = function() {
this.data = null; this.result = '';
this.type = '';
if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) { if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) {
netFilteringResultCacheEntryJunkyard.push(this); netFilteringResultCacheEntryJunkyard.push(this);
} }
@ -70,12 +74,12 @@ NetFilteringResultCacheEntry.prototype.dispose = function() {
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCacheEntry.factory = function(data) { NetFilteringResultCacheEntry.factory = function(result, type, flags) {
var entry = netFilteringResultCacheEntryJunkyard.pop(); var entry = netFilteringResultCacheEntryJunkyard.pop();
if ( entry === undefined ) { if ( entry === undefined ) {
entry = new NetFilteringResultCacheEntry(data); entry = new NetFilteringResultCacheEntry(result, type, flags);
} else { } else {
entry.init(data); entry.init(result, type, flags);
} }
return entry; return entry;
}; };
@ -136,14 +140,16 @@ NetFilteringResultCache.prototype.dispose = function() {
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCache.prototype.add = function(url, data) { NetFilteringResultCache.prototype.add = function(url, result, type, flags) {
var entry = this.urls[url]; var entry = this.urls[url];
if ( entry !== undefined ) { if ( entry !== undefined ) {
entry.data = data; entry.result = result;
entry.type = type;
entry.flags = flags;
entry.time = Date.now(); entry.time = Date.now();
return; return;
} }
this.urls[url] = NetFilteringResultCacheEntry.factory(data); this.urls[url] = NetFilteringResultCacheEntry.factory(result, type, flags);
if ( this.count === 0 ) { if ( this.count === 0 ) {
this.pruneAsync(); this.pruneAsync();
} }
@ -184,7 +190,7 @@ NetFilteringResultCache.prototype.prune = function() {
} }
}; };
// https://www.youtube.com/watch?v=0vTBZzB_gfY // https://www.youtube.com/watch?v=hcVpbsDyOhM
/******************************************************************************/ /******************************************************************************/
@ -201,8 +207,7 @@ NetFilteringResultCache.prototype.pruneAsync = function() {
/******************************************************************************/ /******************************************************************************/
NetFilteringResultCache.prototype.lookup = function(url) { NetFilteringResultCache.prototype.lookup = function(url) {
var entry = this.urls[url]; return this.urls[url];
return entry !== undefined ? entry.data : undefined;
}; };
/******************************************************************************/ /******************************************************************************/
@ -308,7 +313,20 @@ PageStore.prototype.init = function(tabId, pageURL) {
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.reuse = function(pageURL) { PageStore.prototype.reuse = function(pageURL, context) {
// If URL changes without a page reload (more and more common), then we
// need to keep all that we collected for reuse. In particular, not
// doing so was causing a problem in `videos.foxnews.com`: clicking a
// video thumbnail would not work, because the frame hierarchy structure
// was flushed from memory, while not really being flushed on the page.
if ( context === 'tabUpdated' ) {
this.previousPageURL = this.pageURL;
this.pageURL = pageURL;
this.pageHostname = µb.URI.hostnameFromURI(pageURL);
this.pageDomain = µb.URI.domainFromHostname(this.pageHostname) || this.pageHostname;
return this;
}
// A new page is completely reloaded from scratch, reset all.
this.disposeFrameStores(); this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose(); this.netFilteringCache = this.netFilteringCache.dispose();
var previousPageURL = this.pageURL; var previousPageURL = this.pageURL;
@ -383,21 +401,30 @@ PageStore.prototype.getNetFilteringSwitch = function() {
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.filterRequest = function(context, requestType, requestURL) { PageStore.prototype.filterRequest = function(context, requestType, requestURL) {
var result = this.netFilteringCache.lookup(requestURL); var entry = this.netFilteringCache.lookup(requestURL);
if ( result !== undefined ) { if ( entry !== undefined ) {
//console.debug(' cache HIT: PageStore.filterRequest("%s")', requestURL); //console.debug(' cache HIT: PageStore.filterRequest("%s")', requestURL);
return result.slice(result.indexOf('\t') + 1); return entry.result;
} }
//console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL); //console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL);
result = µb.netFilteringEngine.matchString(context, requestURL, requestType); var result = µb.netFilteringEngine.matchString(context, requestURL, requestType);
if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) { if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) {
this.netFilteringCache.add(requestURL, requestType + '\t' + result); this.netFilteringCache.add(requestURL, result, requestType, 0);
} }
return result; return result;
}; };
/******************************************************************************/ /******************************************************************************/
PageStore.prototype.setRequestFlags = function(requestURL, targetBits, valueBits) {
var entry = this.netFilteringCache.lookup(requestURL);
if ( entry !== undefined ) {
entry.flags = (entry.flags & ~targetBits) | (valueBits & targetBits);
}
};
/******************************************************************************/
// false: not blocked // false: not blocked
// true: blocked // true: blocked

View File

@ -19,6 +19,7 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
/* jshint bitwise: false */
/* global chrome, uDom, messaging */ /* global chrome, uDom, messaging */
/******************************************************************************/ /******************************************************************************/
@ -55,6 +56,18 @@ var toPrettyTypeNames = {
/******************************************************************************/ /******************************************************************************/
var chunkify = function(s) {
var chunkSize = 50;
var chunks = [];
while ( s.length ) {
chunks.push(s.slice(0, chunkSize));
s = s.slice(chunkSize);
}
return chunks;
};
/******************************************************************************/
var renderURL = function(url, filter) { var renderURL = function(url, filter) {
var chunkSize = 50; var chunkSize = 50;
// make a regex out of the filter // make a regex out of the filter
@ -75,12 +88,7 @@ var renderURL = function(url, filter) {
; ;
var re = new RegExp(reText, 'gi'); var re = new RegExp(reText, 'gi');
var matches = re.exec(url); var matches = re.exec(url);
var renderedURL = chunkify(url);
var renderedURL = [];
while ( url.length ) {
renderedURL.push(url.slice(0, chunkSize));
url = url.slice(chunkSize);
}
if ( matches && matches[0].length ) { if ( matches && matches[0].length ) {
var index = (re.lastIndex / chunkSize) | 0; var index = (re.lastIndex / chunkSize) | 0;
@ -145,11 +153,11 @@ var renderPageDetails = function(tabId) {
); );
} }
html.push( html.push(
'<tr class="', className, ' requestEntry">', '<tr class="', className, request.flags & 0x01 ? ' logMirrored': '', ' requestEntry">',
'<td>', '<td>',
'<td>', toPrettyTypeNames[request.type] || request.type, '<td>', toPrettyTypeNames[request.type] || request.type,
'<td>', renderURL(request.url, request.reason), '<td>', renderURL(request.url, request.reason),
'<td>', request.reason || '' '<td>', chunkify(request.reason).join('\n')
); );
} }
return html; return html;

View File

@ -46,7 +46,7 @@
if ( !changeInfo.url ) { if ( !changeInfo.url ) {
return; return;
} }
µb.bindTabToPageStats(tabId, changeInfo.url); µb.bindTabToPageStats(tabId, changeInfo.url, 'tabUpdated');
} }
chrome.tabs.onUpdated.addListener(onTabUpdated); chrome.tabs.onUpdated.addListener(onTabUpdated);
@ -103,7 +103,7 @@
if ( pageStore ) { if ( pageStore ) {
if ( pageURL !== pageStore.pageURL || context === 'beforeRequest' ) { if ( pageURL !== pageStore.pageURL || context === 'beforeRequest' ) {
pageStore.reuse(pageURL); pageStore.reuse(pageURL, context);
} }
} else { } else {
pageStore = this.pageStores[tabId] = this.PageStore.factory(tabId, pageURL); pageStore = this.pageStores[tabId] = this.PageStore.factory(tabId, pageURL);

View File

@ -40,20 +40,9 @@ var onBeforeRequest = function(details) {
return; return;
} }
// https://github.com/gorhill/uBlock/issues/206
// https://code.google.com/p/chromium/issues/detail?id=410382
// Work around the issue of Chromium not properly setting the type for
// `object` requests. Unclear whether this issue will be fixed, hence this
// workaround to prevent torch-and-pitchfork mobs because ads are no longer
// blocked in videos.
// onBeforeSendHeaders() will handle this for now.
var requestType = details.type;
if ( requestType === 'other' ) {
return;
}
var µb = µBlock; var µb = µBlock;
var requestURL = details.url; var requestURL = details.url;
var requestType = details.type;
// Special handling for root document. // Special handling for root document.
if ( requestType === 'main_frame' && details.parentFrameId === -1 ) { if ( requestType === 'main_frame' && details.parentFrameId === -1 ) {
@ -64,13 +53,20 @@ var onBeforeRequest = function(details) {
// Commented out until (and if ever) there is a fix for: // Commented out until (and if ever) there is a fix for:
// https://code.google.com/p/chromium/issues/detail?id=410382 // https://code.google.com/p/chromium/issues/detail?id=410382
// //
// Try to transpose generic `other` category into something more // Try to transpose generic `other` category into something more meaningful.
// meaningful. if ( requestType === 'other' ) {
//var µburi = µb.URI.set(requestURL); requestType = µb.transposeType('other', µb.URI.set(requestURL).path);
//var requestPath = µburi.path; // https://github.com/gorhill/uBlock/issues/206
//if ( requestType === 'other' ) { // https://code.google.com/p/chromium/issues/detail?id=410382
// requestType = µb.transposeType(requestType, requestPath); // Work around the issue of Chromium not properly setting the type for
//} // `object` requests. Unclear whether this issue will be fixed, hence
// this workaround to prevent torch-and-pitchfork mobs because ads are
// no longer blocked in videos.
// onBeforeSendHeaders() will handle this for now.
if ( requestType === 'other' ) {
return;
}
}
// Lookup the page store associated with this tab id. // Lookup the page store associated with this tab id.
var pageStore = µb.pageStoreFromTabId(tabId); var pageStore = µb.pageStoreFromTabId(tabId);
@ -105,9 +101,12 @@ var onBeforeRequest = function(details) {
} }
if ( µb.userSettings.experimentalEnabled ) { if ( µb.userSettings.experimentalEnabled ) {
// https://code.google.com/p/chromium/issues/detail?id=387198
// Not all redirects will succeed, until bug above is fixed.
var redirectURL = µb.mirrors.toURL(requestURL, true); var redirectURL = µb.mirrors.toURL(requestURL, true);
if ( redirectURL !== '' ) { if ( redirectURL !== '' ) {
//console.debug('"%s" redirected to "%s..."', requestURL.slice(0, 50), redirectURL.slice(0, 50)); pageStore.setRequestFlags(requestURL, 0x01, 0x01);
console.debug('"%s" redirected to "%s..."', requestURL.slice(0, 50), redirectURL.slice(0, 50));
return { redirectUrl: redirectURL }; return { redirectUrl: redirectURL };
} }
} }
@ -125,7 +124,7 @@ var onBeforeRequest = function(details) {
// Do not use redirection, we need to block outright to be sure the request // Do not use redirection, we need to block outright to be sure the request
// will not be made. There can be no such guarantee with redirection. // will not be made. There can be no such guarantee with redirection.
//console.debug('µBlock> onBeforeRequest()> BLOCK "%s" (%o) because "%s"', details.url, details, result); // console.debug('µBlock> onBeforeRequest()> BLOCK "%s" (%o) because "%s"', details.url, details, result);
return { 'cancel': true }; return { 'cancel': true };
}; };
@ -227,19 +226,22 @@ var cr410382Workaround = function(details) {
var µb = µBlock; var µb = µBlock;
var requestURL = details.url; var requestURL = details.url;
var µburi = µb.URI.set(requestURL); var µburi = µb.URI.set(requestURL);
var requestPath = µburi.path;
// If the type can be successfully transposed, this means the request
// was processed at onBeforeRequest time.
if ( µb.transposeType('other', µburi.path) !== 'other' ) {
return;
}
// Lookup "X-Requested-With" header: this will tell us whether the request // Lookup "X-Requested-With" header: this will tell us whether the request
// is of type "object". // is of type "object".
// Reference: https://code.google.com/p/chromium/issues/detail?id=145090 // Reference: https://code.google.com/p/chromium/issues/detail?id=145090
var requestedWith = headerValue(details.requestHeaders, 'x-requested-with'); var requestedWith = headerValue(details.requestHeaders, 'x-requested-with');
// Try to transpose generic `other` category into something more
// meaningful.
// Reference: https://codereview.chromium.org/451923002/patch/120001/130008 // Reference: https://codereview.chromium.org/451923002/patch/120001/130008
var requestType = requestedWith.indexOf('ShockwaveFlash') !== -1 ? var requestType = requestedWith.indexOf('ShockwaveFlash') !== -1 ?
'object' : 'object' :
µb.transposeType(details.type, requestPath); 'other';
// Lookup the page store associated with this tab id. // Lookup the page store associated with this tab id.
var pageStore = µb.pageStoreFromTabId(details.tabId); var pageStore = µb.pageStoreFromTabId(details.tabId);
@ -364,8 +366,8 @@ chrome.webRequest.onBeforeRequest.addListener(
"script", "script",
"image", "image",
"object", "object",
"xmlhttprequest" "xmlhttprequest",
// "other" // Because cr410382Workaround() "other"
] ]
}, },
[ "blocking" ] [ "blocking" ]

View File

@ -238,7 +238,7 @@
return type; return type;
} }
var ext = path.slice(pos) + '.'; var ext = path.slice(pos) + '.';
if ( '.eot.ttf.otf.svg.woff.'.indexOf(ext) !== -1 ) { if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(ext) !== -1 ) {
return 'stylesheet'; return 'stylesheet';
} }
if ( '.ico.png.gif.jpg.jpeg.'.indexOf(ext) !== -1 ) { if ( '.ico.png.gif.jpg.jpeg.'.indexOf(ext) !== -1 ) {

View File

@ -79,13 +79,16 @@ tr.logBlocked ~ tr td:nth-of-type(3) b {
background-color: rgba(255,0,0,0.1); background-color: rgba(255,0,0,0.1);
} }
tr.logAllowed { tr.logAllowed {
background-color: #f8fff8 !important; background-color: #f8fff8
} }
tr.logAllowed ~ tr td:nth-of-type(3) b { tr.logAllowed ~ tr td:nth-of-type(3) b {
padding: 2px 0; padding: 2px 0;
color: #000; color: #000;
background-color: rgba(0,255,0,0.2); background-color: rgba(0,255,0,0.2);
} }
tr.logMirrored {
background-color: #ffffbb !important;
}
</style> </style>
</head> </head>