diff --git a/js/background.js b/js/background.js index 54d417dfc..e035c5fd7 100644 --- a/js/background.js +++ b/js/background.js @@ -93,7 +93,7 @@ return { }, 'assets/ublock/privacy.txt': { off: true, - title: 'µBlock filters - Privacy', + title: 'µBlock filters – Privacy', group: 'default' } }, diff --git a/js/messaging-handlers.js b/js/messaging-handlers.js index 84cb45204..e207bb4fe 100644 --- a/js/messaging-handlers.js +++ b/js/messaging-handlers.js @@ -529,29 +529,25 @@ var getPageDetails = function(µb, tabId) { var µburi = µb.URI; var dict = pageStore.netFilteringCache.fetchAll(); var r = []; - var details, pos, result, hostname, domain; + var details, hostname, domain; for ( var url in dict ) { if ( dict.hasOwnProperty(url) === false ) { continue; } - details = dict[url].data; - if ( typeof details !== 'string' ) { - continue; - } - pos = details.indexOf('\t'); - result = details.slice(pos + 1); - if ( wantBlocked !== pageStore.boolFromResult(result) ) { + details = dict[url]; + if ( wantBlocked !== pageStore.boolFromResult(details.result) ) { continue; } hasher.appendStr(url); - hasher.appendStr(details); + hasher.appendStr(details.result); hostname = µburi.hostnameFromURI(url); domain = µburi.domainFromHostname(hostname) || hostname; r.push({ - type: details.slice(0, pos), - domain: domain, url: url, - reason: result + domain: domain, + reason: details.result, + type: details.type, + flags: details.flags }); } return r; diff --git a/js/mirrors.js b/js/mirrors.js index 3f7d49479..20d78e50e 100644 --- a/js/mirrors.js +++ b/js/mirrors.js @@ -19,6 +19,7 @@ Home: https://github.com/gorhill/uBlock */ +/* jshint bitwise: false */ /* 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 = { + bytesInUseMax: 5 * 1024 * 1024, + ttl: 21 * 24 * 60 * 60 * 1000, bytesInUse: 0, hitCount: 0 }; @@ -38,6 +48,8 @@ var exports = { 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 = { 'ajax.googleapis.com': /^ajax\.googleapis\.com\/ajax\/libs\//, 'fonts.googleapis.com': /^fonts\.googleapis\.com/, @@ -45,28 +57,35 @@ var mirrorCandidates = { 'cdnjs.cloudflare.com': /^cdnjs\.cloudflare\.com\/ajax\/libs\//, 'code.jquery.com': /^code\.jquery\.com/, '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 bytesInUseMax = 20 * 1024 * 1024; -var ttl = 30 * 24 * 60 * 60 * 1000; var metadataPersistTimer = null; +var bytesInUseMercy = 1 * 1024 * 1024; var metadata = { magicId: magicId, urlKeyToHashMap: {} }; -// Hash to content map -var hashToDataUrlMap = {}; +var hashToContentMap = {}; 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) { this.accessTime = Date.now(); 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. var toUrlKey = function(url) { @@ -111,10 +188,10 @@ var toUrlKey = function(url) { url = url.slice(pos + 3); pos = url.indexOf('/'); if ( pos === -1 ) { - return -1; + return ''; } var re = mirrorCandidates[url.slice(0, pos)]; - if ( typeof re !== 'object' || typeof re.test !== 'function' ) { + if ( typeof re !== 'object' || typeof re.exec !== 'function' ) { return ''; } var matches = re.exec(url); @@ -148,13 +225,84 @@ var normalizeContentType = function(ctin) { /******************************************************************************/ var metadataExists = function(urlKey) { - return typeof urlKey === 'string' && metadata.urlKeyToHashMap.hasOwnProperty(urlKey); + return typeof urlKey === 'string' && + metadata.urlKeyToHashMap.hasOwnProperty(urlKey); }; /******************************************************************************/ 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 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); +var updateMetadataNow = function() { if ( metadataPersistTimer !== null ) { clearTimeout(metadataPersistTimer); } @@ -184,16 +323,45 @@ var updateMetadataAsync = function(urlKey, hash) { /******************************************************************************/ -var updateContent = function(hash, dataURL) { - if ( contentExists(hash) !== false ) { +var updateMetadataAsync = function() { + 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; } - var contentEntry = hashToDataUrlMap[hash] = new ContentEntry(dataURL); + var contentEntry = hashToContentMap[hash] = new ContentEntry(dataURL); exports.bytesInUse += dataURL.length; - var key = 'mirrors_item_' + hash; var bin = {}; - bin[key] = contentEntry; + bin[storageKeyFromHash(hash)] = contentEntry; 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(this.response); var hash = yamd5.end(); - updateMetadataAsync(urlKey, hash); + addMetadata(urlKey, hash); if ( contentExists(hash) ) { //console.debug('mirrors.cacheAsset(): reusing existing content for "%s"', urlKey); return; @@ -226,10 +394,18 @@ var cacheAsset = function(url) { //console.debug('mirrors.cacheAsset(): caching new content for "%s"', urlKey); // Keep original encoding if there was one, otherwise use base64 -- // as the result is somewhat more compact I believe - var dataUrl = contentType.indexOf(';') !== -1 ? - 'data:' + contentType + ',' + encodeURIComponent(this.responseText) : - 'data:' + contentType + ';base64,' + btoa(this.response); - updateContent(hash, dataUrl); + var dataUrl = null; + try { + dataUrl = contentType.indexOf(';') !== -1 ? + '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() { @@ -256,14 +432,18 @@ var toURL = function(url, cache) { } return ''; } + var dataURL = ''; var metadataEntry = metadata.urlKeyToHashMap[urlKey]; - if ( contentExists(metadataEntry.hash) === false ) { - return ''; + if ( contentExists(metadataEntry.hash) ) { + 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(urlKey); - exports.hitCount += 1; - return contentEntry.dataURL; + updateMetadataAsync(); + return dataURL; }; /******************************************************************************/ @@ -271,46 +451,55 @@ var toURL = function(url, cache) { var load = function() { loaded = true; - var loadContent = function(hash) { - var key = 'mirrors_item_' + hash; + var loadContent = function(urlKey, hash) { + var binKey = storageKeyFromHash(hash); 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; } - var contentEntry = bin[key]; - hashToDataUrlMap[hash] = contentEntry; - exports.bytesInUse += contentEntry.dataURL.length; + //console.debug('mirrors.load(): loaded content "%s"', binKey); + var ctEntry = hashToContentMap[hash] = bin[binKey]; + exports.bytesInUse += ctEntry.dataURL.length; }; - var bin = {}; - bin[key] = ''; - chrome.storage.local.get(bin, onContentReady); + chrome.storage.local.get(binKey, onContentReady); }; var onMetadataReady = function(bin) { - if ( chrome.runtime.lastError ) { - return; - } + //console.debug('mirrors.load(): loaded metadata'); metadata = bin.mirrors_metadata; - var hemap = metadata.urlKeyToHashMap; - for ( var urlKey in hemap ) { - if ( hemap.hasOwnProperty(urlKey) === false ) { + var toRemove = []; + var u2hmap = metadata.urlKeyToHashMap; + var hash; + for ( var urlKey in u2hmap ) { + if ( u2hmap.hasOwnProperty(urlKey) === false ) { 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() { - if ( metadataPersistTimer !== null ) { - updateMetadata(); - } + updateMetadataNow(); metadata.urlKeyToHashMap = {}; - hashToDataUrlMap = {}; + hashToContentMap = {}; exports.bytesInUse = 0; exports.hitCount = 0; @@ -332,6 +521,7 @@ exports.toggle = function(on) { // Export API exports.toURL = toURL; +exports.pruneToSize = pruneToSize; return exports; diff --git a/js/pagestore.js b/js/pagestore.js index 9a0f17d78..401d78fd8 100644 --- a/js/pagestore.js +++ b/js/pagestore.js @@ -19,6 +19,7 @@ Home: https://github.com/gorhill/uBlock */ +/* jshint bitwise: false */ /* global µBlock */ /******************************************************************************* @@ -48,21 +49,24 @@ var netFilteringResultCacheEntryJunkyardMax = 200; /******************************************************************************/ -var NetFilteringResultCacheEntry = function(data) { - this.init(data); +var NetFilteringResultCacheEntry = function(result, type, flags) { + this.init(result, type, flags); }; /******************************************************************************/ -NetFilteringResultCacheEntry.prototype.init = function(data) { - this.data = data; +NetFilteringResultCacheEntry.prototype.init = function(result, type, flags) { + this.result = result; + this.type = type; + this.flags = flags; this.time = Date.now(); }; /******************************************************************************/ NetFilteringResultCacheEntry.prototype.dispose = function() { - this.data = null; + this.result = ''; + this.type = ''; if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) { 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(); if ( entry === undefined ) { - entry = new NetFilteringResultCacheEntry(data); + entry = new NetFilteringResultCacheEntry(result, type, flags); } else { - entry.init(data); + entry.init(result, type, flags); } 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]; if ( entry !== undefined ) { - entry.data = data; + entry.result = result; + entry.type = type; + entry.flags = flags; entry.time = Date.now(); return; } - this.urls[url] = NetFilteringResultCacheEntry.factory(data); + this.urls[url] = NetFilteringResultCacheEntry.factory(result, type, flags); if ( this.count === 0 ) { 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) { - var entry = this.urls[url]; - return entry !== undefined ? entry.data : undefined; + return this.urls[url]; }; /******************************************************************************/ @@ -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.netFilteringCache = this.netFilteringCache.dispose(); var previousPageURL = this.pageURL; @@ -383,21 +401,30 @@ PageStore.prototype.getNetFilteringSwitch = function() { /******************************************************************************/ PageStore.prototype.filterRequest = function(context, requestType, requestURL) { - var result = this.netFilteringCache.lookup(requestURL); - if ( result !== undefined ) { + var entry = this.netFilteringCache.lookup(requestURL); + if ( entry !== undefined ) { //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); - result = µb.netFilteringEngine.matchString(context, requestURL, requestType); + var result = µb.netFilteringEngine.matchString(context, requestURL, requestType); if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) { - this.netFilteringCache.add(requestURL, requestType + '\t' + result); + this.netFilteringCache.add(requestURL, result, requestType, 0); } 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 // true: blocked diff --git a/js/stats.js b/js/stats.js index dfd6bb3ae..8c7c5f284 100644 --- a/js/stats.js +++ b/js/stats.js @@ -19,6 +19,7 @@ Home: https://github.com/gorhill/uBlock */ +/* jshint bitwise: false */ /* 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 chunkSize = 50; // make a regex out of the filter @@ -75,12 +88,7 @@ var renderURL = function(url, filter) { ; var re = new RegExp(reText, 'gi'); var matches = re.exec(url); - - var renderedURL = []; - while ( url.length ) { - renderedURL.push(url.slice(0, chunkSize)); - url = url.slice(chunkSize); - } + var renderedURL = chunkify(url); if ( matches && matches[0].length ) { var index = (re.lastIndex / chunkSize) | 0; @@ -145,11 +153,11 @@ var renderPageDetails = function(tabId) { ); } html.push( - '', + '', '', '', toPrettyTypeNames[request.type] || request.type, '', renderURL(request.url, request.reason), - '', request.reason || '' + '', chunkify(request.reason).join('\n') ); } return html; diff --git a/js/tab.js b/js/tab.js index 33eb07e7e..f0394e6c5 100644 --- a/js/tab.js +++ b/js/tab.js @@ -46,7 +46,7 @@ if ( !changeInfo.url ) { return; } - µb.bindTabToPageStats(tabId, changeInfo.url); + µb.bindTabToPageStats(tabId, changeInfo.url, 'tabUpdated'); } chrome.tabs.onUpdated.addListener(onTabUpdated); @@ -103,7 +103,7 @@ if ( pageStore ) { if ( pageURL !== pageStore.pageURL || context === 'beforeRequest' ) { - pageStore.reuse(pageURL); + pageStore.reuse(pageURL, context); } } else { pageStore = this.pageStores[tabId] = this.PageStore.factory(tabId, pageURL); diff --git a/js/traffic.js b/js/traffic.js index 36e25d8a3..f1047d8c7 100644 --- a/js/traffic.js +++ b/js/traffic.js @@ -40,20 +40,9 @@ var onBeforeRequest = function(details) { 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 requestURL = details.url; + var requestType = details.type; // Special handling for root document. 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: // https://code.google.com/p/chromium/issues/detail?id=410382 // - // Try to transpose generic `other` category into something more - // meaningful. - //var µburi = µb.URI.set(requestURL); - //var requestPath = µburi.path; - //if ( requestType === 'other' ) { - // requestType = µb.transposeType(requestType, requestPath); - //} + // Try to transpose generic `other` category into something more meaningful. + if ( requestType === 'other' ) { + requestType = µb.transposeType('other', µb.URI.set(requestURL).path); + // 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. + if ( requestType === 'other' ) { + return; + } + } // Lookup the page store associated with this tab id. var pageStore = µb.pageStoreFromTabId(tabId); @@ -105,9 +101,12 @@ var onBeforeRequest = function(details) { } 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); 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 }; } } @@ -125,7 +124,7 @@ var onBeforeRequest = function(details) { // 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. - //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 }; }; @@ -227,19 +226,22 @@ var cr410382Workaround = function(details) { var µb = µBlock; var requestURL = details.url; 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 // is of type "object". // Reference: https://code.google.com/p/chromium/issues/detail?id=145090 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 var requestType = requestedWith.indexOf('ShockwaveFlash') !== -1 ? 'object' : - µb.transposeType(details.type, requestPath); + 'other'; // Lookup the page store associated with this tab id. var pageStore = µb.pageStoreFromTabId(details.tabId); @@ -364,8 +366,8 @@ chrome.webRequest.onBeforeRequest.addListener( "script", "image", "object", - "xmlhttprequest" - // "other" // Because cr410382Workaround() + "xmlhttprequest", + "other" ] }, [ "blocking" ] diff --git a/js/ublock.js b/js/ublock.js index c95f298c6..47eb7edea 100644 --- a/js/ublock.js +++ b/js/ublock.js @@ -238,7 +238,7 @@ return type; } 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'; } if ( '.ico.png.gif.jpg.jpeg.'.indexOf(ext) !== -1 ) { diff --git a/stats.html b/stats.html index 9ec76e873..628d8691e 100644 --- a/stats.html +++ b/stats.html @@ -79,13 +79,16 @@ tr.logBlocked ~ tr td:nth-of-type(3) b { background-color: rgba(255,0,0,0.1); } tr.logAllowed { - background-color: #f8fff8 !important; + background-color: #f8fff8 } tr.logAllowed ~ tr td:nth-of-type(3) b { padding: 2px 0; color: #000; background-color: rgba(0,255,0,0.2); } +tr.logMirrored { + background-color: #ffffbb !important; + }