2014-06-24 00:42:43 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
|
2016-03-07 15:55:04 +01:00
|
|
|
uBlock Origin - a browser extension to block requests.
|
2018-07-22 16:47:02 +02:00
|
|
|
Copyright (C) 2014-present Raymond Hill
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
|
|
|
|
|
|
Home: https://github.com/gorhill/uBlock
|
|
|
|
*/
|
|
|
|
|
2016-07-01 04:03:29 +02:00
|
|
|
'use strict';
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
A PageRequestStore object is used to store net requests in two ways:
|
|
|
|
|
|
|
|
To record distinct net requests
|
|
|
|
To create a log of net requests
|
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.PageStore = (function() {
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var µb = µBlock;
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
/******************************************************************************/
|
2014-06-24 00:42:43 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
// To mitigate memory churning
|
2017-08-03 16:18:05 +02:00
|
|
|
var netFilteringCacheJunkyard = [],
|
|
|
|
netFilteringCacheJunkyardMax = 10;
|
2014-09-14 22:20:40 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
var NetFilteringResultCache = function() {
|
|
|
|
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
|
|
|
|
this.init();
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
NetFilteringResultCache.prototype.shelfLife = 15 * 1000;
|
2014-09-14 22:20:40 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
NetFilteringResultCache.factory = function() {
|
|
|
|
var entry = netFilteringCacheJunkyard.pop();
|
|
|
|
if ( entry === undefined ) {
|
|
|
|
entry = new NetFilteringResultCache();
|
|
|
|
} else {
|
|
|
|
entry.init();
|
|
|
|
}
|
|
|
|
return entry;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
NetFilteringResultCache.prototype.init = function() {
|
2017-08-03 16:18:05 +02:00
|
|
|
this.blocked = new Map();
|
|
|
|
this.results = new Map();
|
|
|
|
this.hash = 0;
|
2015-01-06 14:01:15 +01:00
|
|
|
this.timer = null;
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
NetFilteringResultCache.prototype.dispose = function() {
|
2015-01-22 03:46:11 +01:00
|
|
|
this.empty();
|
2014-09-14 22:20:40 +02:00
|
|
|
if ( netFilteringCacheJunkyard.length < netFilteringCacheJunkyardMax ) {
|
|
|
|
netFilteringCacheJunkyard.push(this);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
NetFilteringResultCache.prototype.rememberResult = function(context, result, logData) {
|
|
|
|
if ( this.results.size === 0 ) {
|
|
|
|
this.pruneAsync();
|
2014-09-14 22:20:40 +02:00
|
|
|
}
|
2017-08-03 16:18:05 +02:00
|
|
|
var key = context.pageHostname + ' ' + context.requestType + ' ' + context.requestURL;
|
|
|
|
this.results.set(key, {
|
|
|
|
result: result,
|
|
|
|
logData: logData,
|
|
|
|
tstamp: Date.now()
|
|
|
|
});
|
|
|
|
if ( result !== 1 ) { return; }
|
|
|
|
var now = Date.now();
|
|
|
|
this.blocked.set(key, now);
|
|
|
|
this.hash = now;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
NetFilteringResultCache.prototype.rememberBlock = function(details) {
|
|
|
|
if ( this.blocked.size === 0 ) {
|
2014-09-14 22:20:40 +02:00
|
|
|
this.pruneAsync();
|
|
|
|
}
|
2017-08-03 16:18:05 +02:00
|
|
|
var now = Date.now();
|
|
|
|
this.blocked.set(
|
|
|
|
details.pageHostname + ' ' + details.requestType + ' ' + details.requestURL,
|
|
|
|
now
|
|
|
|
);
|
|
|
|
this.hash = now;
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-01-22 03:46:11 +01:00
|
|
|
NetFilteringResultCache.prototype.empty = function() {
|
2017-08-03 16:18:05 +02:00
|
|
|
this.blocked.clear();
|
|
|
|
this.results.clear();
|
|
|
|
this.hash = 0;
|
2015-01-22 03:46:11 +01:00
|
|
|
if ( this.timer !== null ) {
|
|
|
|
clearTimeout(this.timer);
|
|
|
|
this.timer = null;
|
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
NetFilteringResultCache.prototype.pruneAsync = function() {
|
|
|
|
if ( this.timer === null ) {
|
|
|
|
this.timer = vAPI.setTimeout(this.boundPruneAsyncCallback, this.shelfLife * 2);
|
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
NetFilteringResultCache.prototype.pruneAsyncCallback = function() {
|
|
|
|
this.timer = null;
|
|
|
|
var obsolete = Date.now() - this.shelfLife,
|
|
|
|
entry;
|
|
|
|
for ( entry of this.blocked ) {
|
|
|
|
if ( entry[1] <= obsolete ) {
|
|
|
|
this.results.delete(entry[0]);
|
|
|
|
this.blocked.delete(entry[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for ( entry of this.results ) {
|
|
|
|
if ( entry[1].tstamp <= obsolete ) {
|
|
|
|
this.results.delete(entry[0]);
|
2014-07-30 07:05:35 +02:00
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
}
|
2017-08-03 16:18:05 +02:00
|
|
|
if ( this.blocked.size !== 0 || this.results.size !== 0 ) {
|
2014-09-14 22:20:40 +02:00
|
|
|
this.pruneAsync();
|
2014-07-30 07:05:35 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
NetFilteringResultCache.prototype.lookupResult = function(context) {
|
|
|
|
return this.results.get(
|
|
|
|
context.pageHostname + ' ' +
|
|
|
|
context.requestType + ' ' +
|
|
|
|
context.requestURL
|
|
|
|
);
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
NetFilteringResultCache.prototype.lookupAllBlocked = function(hostname) {
|
|
|
|
var result = [],
|
|
|
|
pos;
|
|
|
|
for ( var entry of this.blocked ) {
|
|
|
|
pos = entry[0].indexOf(' ');
|
|
|
|
if ( entry[0].slice(0, pos) === hostname ) {
|
|
|
|
result[result.length] = entry[0].slice(pos + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-04-09 00:46:08 +02:00
|
|
|
// Frame stores are used solely to associate a URL with a frame id. The
|
|
|
|
// name `pageHostname` is used because of historical reasons. A more
|
|
|
|
// appropriate name is `frameHostname` -- something to do in a future
|
|
|
|
// refactoring.
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
// To mitigate memory churning
|
|
|
|
var frameStoreJunkyard = [];
|
|
|
|
var frameStoreJunkyardMax = 50;
|
|
|
|
|
2014-07-30 07:05:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-03-07 15:55:04 +01:00
|
|
|
var FrameStore = function(frameURL) {
|
|
|
|
this.init(frameURL);
|
2014-07-30 07:05:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-03-07 15:55:04 +01:00
|
|
|
FrameStore.factory = function(frameURL) {
|
2014-09-14 22:20:40 +02:00
|
|
|
var entry = frameStoreJunkyard.pop();
|
|
|
|
if ( entry === undefined ) {
|
2016-03-07 15:55:04 +01:00
|
|
|
return new FrameStore(frameURL);
|
2014-09-14 22:20:40 +02:00
|
|
|
}
|
2016-03-07 15:55:04 +01:00
|
|
|
return entry.init(frameURL);
|
2014-09-14 22:20:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-03-07 15:55:04 +01:00
|
|
|
FrameStore.prototype.init = function(frameURL) {
|
2014-07-30 07:05:35 +02:00
|
|
|
var µburi = µb.URI;
|
|
|
|
this.pageHostname = µburi.hostnameFromURI(frameURL);
|
2014-10-07 22:30:40 +02:00
|
|
|
this.pageDomain = µburi.domainFromHostname(this.pageHostname) || this.pageHostname;
|
2014-07-30 07:05:35 +02:00
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
FrameStore.prototype.dispose = function() {
|
2015-04-09 00:46:08 +02:00
|
|
|
this.pageHostname = this.pageDomain = '';
|
2014-09-14 22:20:40 +02:00
|
|
|
if ( frameStoreJunkyard.length < frameStoreJunkyardMax ) {
|
|
|
|
frameStoreJunkyard.push(this);
|
|
|
|
}
|
|
|
|
return null;
|
2014-07-30 07:05:35 +02:00
|
|
|
};
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
/******************************************************************************/
|
2014-07-30 07:05:35 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
// To mitigate memory churning
|
|
|
|
var pageStoreJunkyard = [];
|
|
|
|
var pageStoreJunkyardMax = 10;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-11-07 17:31:19 +01:00
|
|
|
var PageStore = function(tabId, context) {
|
|
|
|
this.init(tabId, context);
|
2016-10-08 16:15:31 +02:00
|
|
|
this.journal = [];
|
|
|
|
this.journalTimer = null;
|
|
|
|
this.journalLastCommitted = this.journalLastUncommitted = undefined;
|
|
|
|
this.journalLastUncommittedURL = undefined;
|
2014-07-30 07:05:35 +02:00
|
|
|
};
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-11-07 17:31:19 +01:00
|
|
|
PageStore.factory = function(tabId, context) {
|
2014-09-14 22:20:40 +02:00
|
|
|
var entry = pageStoreJunkyard.pop();
|
|
|
|
if ( entry === undefined ) {
|
2017-11-07 17:31:19 +01:00
|
|
|
entry = new PageStore(tabId, context);
|
2014-09-14 22:20:40 +02:00
|
|
|
} else {
|
2017-11-07 17:31:19 +01:00
|
|
|
entry.init(tabId, context);
|
2014-09-14 22:20:40 +02:00
|
|
|
}
|
|
|
|
return entry;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-11-07 17:31:19 +01:00
|
|
|
// https://github.com/gorhill/uBlock/issues/3201
|
|
|
|
// The context is used to determine whether we report behavior change to the
|
|
|
|
// logger.
|
|
|
|
|
|
|
|
PageStore.prototype.init = function(tabId, context) {
|
2015-12-02 06:59:51 +01:00
|
|
|
var tabContext = µb.tabContextManager.mustLookup(tabId);
|
2014-06-24 00:42:43 +02:00
|
|
|
this.tabId = tabId;
|
2016-01-17 19:30:43 +01:00
|
|
|
|
|
|
|
// If we are navigating from-to same site, remember whether large
|
|
|
|
// media elements were temporarily allowed.
|
|
|
|
if (
|
|
|
|
typeof this.allowLargeMediaElementsUntil !== 'number' ||
|
|
|
|
tabContext.rootHostname !== this.tabHostname
|
|
|
|
) {
|
|
|
|
this.allowLargeMediaElementsUntil = 0;
|
|
|
|
}
|
|
|
|
|
2015-04-09 00:46:08 +02:00
|
|
|
this.tabHostname = tabContext.rootHostname;
|
2015-05-16 16:15:02 +02:00
|
|
|
this.title = tabContext.rawURL;
|
|
|
|
this.rawURL = tabContext.rawURL;
|
2016-10-08 16:15:31 +02:00
|
|
|
this.hostnameToCountMap = new Map();
|
2015-01-10 17:23:28 +01:00
|
|
|
this.contentLastModified = 0;
|
2018-02-26 19:59:16 +01:00
|
|
|
this.frames = new Map();
|
2017-05-12 16:35:11 +02:00
|
|
|
this.logData = undefined;
|
2014-07-14 17:24:59 +02:00
|
|
|
this.perLoadBlockedRequestCount = 0;
|
|
|
|
this.perLoadAllowedRequestCount = 0;
|
2015-04-05 18:03:14 +02:00
|
|
|
this.hiddenElementCount = ''; // Empty string means "unknown"
|
2015-06-10 15:23:48 +02:00
|
|
|
this.remoteFontCount = 0;
|
2016-01-04 16:48:28 +01:00
|
|
|
this.popupBlockedCount = 0;
|
2016-01-17 19:30:43 +01:00
|
|
|
this.largeMediaCount = 0;
|
|
|
|
this.largeMediaTimer = null;
|
2014-09-14 22:20:40 +02:00
|
|
|
this.netFilteringCache = NetFilteringResultCache.factory();
|
2016-10-14 16:06:34 +02:00
|
|
|
this.internalRedirectionCount = 0;
|
2015-01-06 14:01:15 +01:00
|
|
|
|
2017-11-07 17:31:19 +01:00
|
|
|
this.noCosmeticFiltering = µb.hnSwitches.evaluateZ(
|
|
|
|
'no-cosmetic-filtering',
|
|
|
|
tabContext.rootHostname
|
|
|
|
) === true;
|
|
|
|
if (
|
|
|
|
this.noCosmeticFiltering &&
|
|
|
|
µb.logger.isEnabled() &&
|
|
|
|
context === 'tabCommitted'
|
|
|
|
) {
|
2015-06-09 16:27:08 +02:00
|
|
|
µb.logger.writeOne(
|
|
|
|
tabId,
|
2016-08-12 14:55:35 +02:00
|
|
|
'cosmetic',
|
2017-05-12 16:35:11 +02:00
|
|
|
µb.hnSwitches.toLogData(),
|
2016-08-12 14:55:35 +02:00
|
|
|
'dom',
|
2015-06-09 16:27:08 +02:00
|
|
|
tabContext.rawURL,
|
|
|
|
this.tabHostname,
|
|
|
|
this.tabHostname
|
|
|
|
);
|
|
|
|
}
|
2016-08-12 14:55:35 +02:00
|
|
|
|
|
|
|
// Support `generichide` filter option.
|
|
|
|
this.noGenericCosmeticFiltering = this.noCosmeticFiltering;
|
|
|
|
if ( this.noGenericCosmeticFiltering !== true ) {
|
2018-07-22 16:47:02 +02:00
|
|
|
let result = µb.staticNetFilteringEngine.matchStringGenericHide(
|
|
|
|
tabContext.normalURL
|
2016-11-08 13:13:26 +01:00
|
|
|
);
|
2017-05-12 16:35:11 +02:00
|
|
|
this.noGenericCosmeticFiltering = result === 2;
|
2017-11-07 17:31:19 +01:00
|
|
|
if (
|
|
|
|
result !== 0 &&
|
|
|
|
µb.logger.isEnabled() &&
|
|
|
|
context === 'tabCommitted'
|
|
|
|
) {
|
2016-08-12 14:55:35 +02:00
|
|
|
µb.logger.writeOne(
|
|
|
|
tabId,
|
|
|
|
'net',
|
2017-05-12 16:35:11 +02:00
|
|
|
µb.staticNetFilteringEngine.toLogData(),
|
2016-10-27 14:33:19 +02:00
|
|
|
'generichide',
|
2016-08-12 14:55:35 +02:00
|
|
|
tabContext.rawURL,
|
|
|
|
this.tabHostname,
|
|
|
|
this.tabHostname
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-14 17:24:59 +02:00
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-04-09 00:46:08 +02:00
|
|
|
PageStore.prototype.reuse = function(context) {
|
2015-04-09 17:19:31 +02:00
|
|
|
// When force refreshing a page, the page store data needs to be reset.
|
2015-03-22 01:30:00 +01:00
|
|
|
|
2015-03-26 00:28:22 +01:00
|
|
|
// If the hostname changes, we can't merely just update the context.
|
2015-12-02 06:59:51 +01:00
|
|
|
var tabContext = µb.tabContextManager.mustLookup(this.tabId);
|
2015-04-09 00:46:08 +02:00
|
|
|
if ( tabContext.rootHostname !== this.tabHostname ) {
|
2015-03-26 00:28:22 +01:00
|
|
|
context = '';
|
|
|
|
}
|
|
|
|
|
2014-10-17 21:44:19 +02:00
|
|
|
// If URL changes without a page reload (more and more common), then we
|
2014-10-02 22:45:26 +02:00
|
|
|
// 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' ) {
|
2015-04-07 03:26:05 +02:00
|
|
|
// As part of https://github.com/chrisaljoudi/uBlock/issues/405
|
2014-12-08 18:37:35 +01:00
|
|
|
// URL changed, force a re-evaluation of filtering switch
|
2015-05-16 16:15:02 +02:00
|
|
|
this.rawURL = tabContext.rawURL;
|
2014-10-02 22:45:26 +02:00
|
|
|
return this;
|
|
|
|
}
|
2015-04-09 00:46:08 +02:00
|
|
|
|
2014-10-02 22:45:26 +02:00
|
|
|
// A new page is completely reloaded from scratch, reset all.
|
2016-01-17 19:30:43 +01:00
|
|
|
if ( this.largeMediaTimer !== null ) {
|
|
|
|
clearTimeout(this.largeMediaTimer);
|
|
|
|
this.largeMediaTimer = null;
|
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
this.disposeFrameStores();
|
|
|
|
this.netFilteringCache = this.netFilteringCache.dispose();
|
2017-11-07 17:31:19 +01:00
|
|
|
this.init(this.tabId, context);
|
2014-06-24 00:42:43 +02:00
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2014-09-14 22:20:40 +02:00
|
|
|
// https://www.youtube.com/watch?v=dltNSbOupgE
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
PageStore.prototype.dispose = function() {
|
2015-05-16 16:15:02 +02:00
|
|
|
this.tabHostname = '';
|
|
|
|
this.title = '';
|
|
|
|
this.rawURL = '';
|
2014-12-30 22:36:29 +01:00
|
|
|
this.hostnameToCountMap = null;
|
2016-01-17 19:30:43 +01:00
|
|
|
this.allowLargeMediaElementsUntil = 0;
|
|
|
|
if ( this.largeMediaTimer !== null ) {
|
|
|
|
clearTimeout(this.largeMediaTimer);
|
|
|
|
this.largeMediaTimer = null;
|
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
this.disposeFrameStores();
|
|
|
|
this.netFilteringCache = this.netFilteringCache.dispose();
|
2016-10-08 16:15:31 +02:00
|
|
|
if ( this.journalTimer !== null ) {
|
|
|
|
clearTimeout(this.journalTimer);
|
|
|
|
this.journalTimer = null;
|
|
|
|
}
|
|
|
|
this.journal = [];
|
|
|
|
this.journalLastUncommittedURL = undefined;
|
2014-09-14 22:20:40 +02:00
|
|
|
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
pageStoreJunkyard.push(this);
|
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
PageStore.prototype.disposeFrameStores = function() {
|
2018-02-26 19:59:16 +01:00
|
|
|
for ( var frameStore of this.frames.values() ) {
|
|
|
|
frameStore.dispose();
|
2014-09-14 22:20:40 +02:00
|
|
|
}
|
2018-02-26 19:59:16 +01:00
|
|
|
this.frames.clear();
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-02-25 20:15:36 +01:00
|
|
|
PageStore.prototype.getFrame = function(frameId) {
|
2018-02-26 19:59:16 +01:00
|
|
|
return this.frames.get(frameId) || null;
|
2014-07-30 07:05:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-02-25 20:15:36 +01:00
|
|
|
PageStore.prototype.setFrame = function(frameId, frameURL) {
|
2018-02-26 19:59:16 +01:00
|
|
|
var frameStore = this.frames.get(frameId);
|
|
|
|
if ( frameStore !== undefined ) {
|
2016-03-07 15:55:04 +01:00
|
|
|
frameStore.init(frameURL);
|
2015-02-25 20:15:36 +01:00
|
|
|
} else {
|
2018-02-26 19:59:16 +01:00
|
|
|
this.frames.set(frameId, FrameStore.factory(frameURL));
|
2015-02-25 20:15:36 +01:00
|
|
|
}
|
2014-07-30 07:05:35 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-04-09 00:46:08 +02:00
|
|
|
PageStore.prototype.createContextFromPage = function() {
|
2016-07-01 04:03:29 +02:00
|
|
|
var context = µb.tabContextManager.createContext(this.tabId);
|
2015-04-09 00:46:08 +02:00
|
|
|
context.pageHostname = context.rootHostname;
|
|
|
|
context.pageDomain = context.rootDomain;
|
|
|
|
return context;
|
|
|
|
};
|
|
|
|
|
|
|
|
PageStore.prototype.createContextFromFrameId = function(frameId) {
|
2016-07-01 04:03:29 +02:00
|
|
|
var context = µb.tabContextManager.createContext(this.tabId);
|
2018-02-26 19:59:16 +01:00
|
|
|
var frameStore = this.frames.get(frameId);
|
|
|
|
if ( frameStore !== undefined ) {
|
2015-04-09 00:46:08 +02:00
|
|
|
context.pageHostname = frameStore.pageHostname;
|
|
|
|
context.pageDomain = frameStore.pageDomain;
|
|
|
|
} else {
|
|
|
|
context.pageHostname = context.rootHostname;
|
|
|
|
context.pageDomain = context.rootDomain;
|
|
|
|
}
|
|
|
|
return context;
|
|
|
|
};
|
|
|
|
|
|
|
|
PageStore.prototype.createContextFromFrameHostname = function(frameHostname) {
|
2016-07-01 04:03:29 +02:00
|
|
|
var context = µb.tabContextManager.createContext(this.tabId);
|
2015-04-09 00:46:08 +02:00
|
|
|
context.pageHostname = frameHostname;
|
|
|
|
context.pageDomain = µb.URI.domainFromHostname(frameHostname) || frameHostname;
|
|
|
|
return context;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-08-02 17:40:27 +02:00
|
|
|
PageStore.prototype.getNetFilteringSwitch = function() {
|
2015-12-02 06:59:51 +01:00
|
|
|
return µb.tabContextManager.mustLookup(this.tabId).getNetFilteringSwitch();
|
2014-08-02 17:40:27 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-02-06 05:14:12 +01:00
|
|
|
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
|
2016-08-12 14:55:35 +02:00
|
|
|
return this.noCosmeticFiltering !== true;
|
2015-02-06 05:14:12 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
PageStore.prototype.getGenericCosmeticFilteringSwitch = function() {
|
2016-08-12 14:55:35 +02:00
|
|
|
return this.noGenericCosmeticFiltering !== true &&
|
|
|
|
this.noCosmeticFiltering !== true;
|
2015-01-09 03:04:48 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-01-22 03:46:11 +01:00
|
|
|
PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) {
|
|
|
|
µb.toggleNetFilteringSwitch(url, scope, state);
|
|
|
|
this.netFilteringCache.empty();
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-11-08 21:53:08 +01:00
|
|
|
PageStore.prototype.injectLargeMediaElementScriptlet = function() {
|
|
|
|
this.largeMediaTimer = null;
|
|
|
|
µb.scriptlets.injectDeep(
|
|
|
|
this.tabId,
|
|
|
|
'load-large-media-interactive'
|
|
|
|
);
|
|
|
|
µb.contextMenu.update(this.tabId);
|
|
|
|
};
|
2016-01-17 19:30:43 +01:00
|
|
|
|
2017-09-12 17:43:43 +02:00
|
|
|
PageStore.prototype.temporarilyAllowLargeMediaElements = function(state) {
|
2016-01-17 19:30:43 +01:00
|
|
|
this.largeMediaCount = 0;
|
|
|
|
µb.contextMenu.update(this.tabId);
|
2017-09-12 17:43:43 +02:00
|
|
|
this.allowLargeMediaElementsUntil = state ? Date.now() + 86400000 : 0;
|
2016-01-17 19:30:43 +01:00
|
|
|
µb.scriptlets.injectDeep(this.tabId, 'load-large-media-all');
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-10-08 16:15:31 +02:00
|
|
|
// https://github.com/gorhill/uBlock/issues/2053
|
|
|
|
// There is no way around using journaling to ensure we deal properly with
|
|
|
|
// potentially out of order navigation events vs. network request events.
|
|
|
|
|
|
|
|
PageStore.prototype.journalAddRequest = function(hostname, result) {
|
|
|
|
if ( hostname === '' ) { return; }
|
|
|
|
this.journal.push(
|
|
|
|
hostname,
|
2017-05-12 16:35:11 +02:00
|
|
|
result === 1 ? 0x00000001 : 0x00010000
|
2016-10-08 16:15:31 +02:00
|
|
|
);
|
|
|
|
if ( this.journalTimer === null ) {
|
|
|
|
this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
PageStore.prototype.journalAddRootFrame = function(type, url) {
|
|
|
|
if ( type === 'committed' ) {
|
|
|
|
this.journalLastCommitted = this.journal.length;
|
|
|
|
if (
|
|
|
|
this.journalLastUncommitted !== undefined &&
|
|
|
|
this.journalLastUncommitted < this.journalLastCommitted &&
|
|
|
|
this.journalLastUncommittedURL === url
|
|
|
|
) {
|
|
|
|
this.journalLastCommitted = this.journalLastUncommitted;
|
|
|
|
this.journalLastUncommitted = undefined;
|
|
|
|
}
|
|
|
|
} else if ( type === 'uncommitted' ) {
|
|
|
|
this.journalLastUncommitted = this.journal.length;
|
|
|
|
this.journalLastUncommittedURL = url;
|
|
|
|
}
|
|
|
|
if ( this.journalTimer !== null ) {
|
|
|
|
clearTimeout(this.journalTimer);
|
|
|
|
}
|
|
|
|
this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000);
|
|
|
|
};
|
|
|
|
|
|
|
|
PageStore.prototype.journalProcess = function(fromTimer) {
|
|
|
|
if ( !fromTimer ) {
|
|
|
|
clearTimeout(this.journalTimer);
|
|
|
|
}
|
|
|
|
this.journalTimer = null;
|
|
|
|
|
|
|
|
var journal = this.journal,
|
|
|
|
i, n = journal.length,
|
|
|
|
hostname, count, hostnameCounts,
|
|
|
|
aggregateCounts = 0,
|
|
|
|
now = Date.now(),
|
|
|
|
pivot = this.journalLastCommitted || 0;
|
|
|
|
|
|
|
|
// Everything after pivot originates from current page.
|
|
|
|
for ( i = pivot; i < n; i += 2 ) {
|
|
|
|
hostname = journal[i];
|
|
|
|
hostnameCounts = this.hostnameToCountMap.get(hostname);
|
|
|
|
if ( hostnameCounts === undefined ) {
|
|
|
|
hostnameCounts = 0;
|
|
|
|
this.contentLastModified = now;
|
|
|
|
}
|
|
|
|
count = journal[i+1];
|
|
|
|
this.hostnameToCountMap.set(hostname, hostnameCounts + count);
|
|
|
|
aggregateCounts += count;
|
|
|
|
}
|
|
|
|
this.perLoadBlockedRequestCount += aggregateCounts & 0xFFFF;
|
|
|
|
this.perLoadAllowedRequestCount += aggregateCounts >>> 16 & 0xFFFF;
|
|
|
|
this.journalLastCommitted = undefined;
|
|
|
|
|
|
|
|
// https://github.com/chrisaljoudi/uBlock/issues/905#issuecomment-76543649
|
|
|
|
// No point updating the badge if it's not being displayed.
|
|
|
|
if ( (aggregateCounts & 0xFFFF) && µb.userSettings.showIconBadge ) {
|
2018-05-08 15:43:25 +02:00
|
|
|
µb.updateToolbarIcon(this.tabId, 0x02);
|
2016-10-08 16:15:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Everything before pivot does not originate from current page -- we still
|
|
|
|
// need to bump global blocked/allowed counts.
|
|
|
|
for ( i = 0; i < pivot; i += 2 ) {
|
|
|
|
aggregateCounts += journal[i+1];
|
|
|
|
}
|
|
|
|
if ( aggregateCounts !== 0 ) {
|
|
|
|
µb.localSettings.blockedRequestCount += aggregateCounts & 0xFFFF;
|
|
|
|
µb.localSettings.allowedRequestCount += aggregateCounts >>> 16 & 0xFFFF;
|
|
|
|
µb.localSettingsLastModified = now;
|
|
|
|
}
|
|
|
|
journal.length = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-12-28 16:07:43 +01:00
|
|
|
PageStore.prototype.filterRequest = function(context) {
|
2017-05-12 16:35:11 +02:00
|
|
|
this.logData = undefined;
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
if ( this.getNetFilteringSwitch() === false ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-10 15:23:48 +02:00
|
|
|
var requestType = context.requestType;
|
2015-01-24 18:06:22 +01:00
|
|
|
|
2017-10-19 15:35:28 +02:00
|
|
|
if ( requestType === 'csp_report' && this.filterCSPReport(context) === 1 ) {
|
|
|
|
return 1;
|
2016-10-14 16:06:34 +02:00
|
|
|
}
|
|
|
|
|
2017-10-19 15:35:28 +02:00
|
|
|
if ( requestType.endsWith('font') && this.filterFont(context) === 1 ) {
|
|
|
|
return 1;
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
2014-12-28 16:07:43 +01:00
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
var cacheableResult = this.cacheableResults[requestType] === true;
|
|
|
|
|
|
|
|
if ( cacheableResult ) {
|
|
|
|
var entry = this.netFilteringCache.lookupResult(context);
|
|
|
|
if ( entry !== undefined ) {
|
|
|
|
this.logData = entry.logData;
|
|
|
|
return entry.result;
|
|
|
|
}
|
2014-09-14 22:20:40 +02:00
|
|
|
}
|
2014-12-28 16:07:43 +01:00
|
|
|
|
2016-10-14 16:06:34 +02:00
|
|
|
// Dynamic URL filtering.
|
2017-05-12 16:35:11 +02:00
|
|
|
var result = µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType);
|
|
|
|
if ( result !== 0 && µb.logger.isEnabled() ) {
|
|
|
|
this.logData = µb.sessionURLFiltering.toLogData();
|
|
|
|
}
|
2015-01-06 14:01:15 +01:00
|
|
|
|
2016-10-14 16:06:34 +02:00
|
|
|
// Dynamic hostname/type filtering.
|
2017-05-12 16:35:11 +02:00
|
|
|
if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
|
2017-08-03 16:18:05 +02:00
|
|
|
result = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, requestType);
|
2017-06-14 14:32:12 +02:00
|
|
|
if ( result !== 0 && result !== 3 && µb.logger.isEnabled() ) {
|
2017-05-12 16:35:11 +02:00
|
|
|
this.logData = µb.sessionFirewall.toLogData();
|
2017-08-03 16:18:05 +02:00
|
|
|
}
|
2015-01-06 14:01:15 +01:00
|
|
|
}
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
// Static filtering has lowest precedence.
|
2017-05-12 16:35:11 +02:00
|
|
|
if ( result === 0 || result === 3 ) {
|
|
|
|
result = µb.staticNetFilteringEngine.matchString(context);
|
|
|
|
if ( result !== 0 && µb.logger.isEnabled() ) {
|
|
|
|
this.logData = µb.staticNetFilteringEngine.toLogData();
|
2015-06-09 16:27:08 +02:00
|
|
|
}
|
2015-01-06 14:01:15 +01:00
|
|
|
}
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
if ( cacheableResult ) {
|
|
|
|
this.netFilteringCache.rememberResult(context, result, this.logData);
|
|
|
|
} else if ( result === 1 && this.collapsibleResources[requestType] === true ) {
|
|
|
|
this.netFilteringCache.rememberBlock(context, true);
|
|
|
|
}
|
2014-12-28 16:07:43 +01:00
|
|
|
|
2015-01-06 14:01:15 +01:00
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
PageStore.prototype.cacheableResults = {
|
|
|
|
sub_frame: true
|
|
|
|
};
|
|
|
|
|
|
|
|
PageStore.prototype.collapsibleResources = {
|
|
|
|
image: true,
|
|
|
|
media: true,
|
|
|
|
object: true,
|
|
|
|
sub_frame: true
|
|
|
|
};
|
|
|
|
|
2015-01-24 18:06:22 +01:00
|
|
|
/******************************************************************************/
|
|
|
|
|
2017-10-19 15:35:28 +02:00
|
|
|
PageStore.prototype.filterCSPReport = function(context) {
|
2017-11-22 13:41:33 +01:00
|
|
|
if ( µb.hnSwitches.evaluateZ('no-csp-reports', context.requestHostname) ) {
|
2017-10-19 15:35:28 +02:00
|
|
|
if ( µb.logger.isEnabled() ) {
|
|
|
|
this.logData = µb.hnSwitches.toLogData();
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// https://github.com/gorhill/uBlock/issues/3140
|
|
|
|
// Special handling of CSP reports if and only if these can't be filtered
|
|
|
|
// natively.
|
|
|
|
if (
|
|
|
|
vAPI.net.nativeCSPReportFiltering !== true &&
|
|
|
|
this.internalRedirectionCount !== 0
|
|
|
|
) {
|
|
|
|
if ( µb.logger.isEnabled() ) {
|
|
|
|
this.logData = {
|
|
|
|
result: 1,
|
|
|
|
source: 'global',
|
|
|
|
raw: 'no-spurious-csp-report'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
PageStore.prototype.filterFont = function(context) {
|
|
|
|
if ( context.requestType === 'font' ) {
|
|
|
|
this.remoteFontCount += 1;
|
|
|
|
}
|
|
|
|
if ( µb.hnSwitches.evaluateZ('no-remote-fonts', context.rootHostname) !== false ) {
|
|
|
|
if ( µb.logger.isEnabled() ) {
|
|
|
|
this.logData = µb.hnSwitches.toLogData();
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-11-08 21:53:08 +01:00
|
|
|
// The caller is responsible to check whether filtering is enabled or not.
|
|
|
|
|
|
|
|
PageStore.prototype.filterLargeMediaElement = function(size) {
|
2017-05-12 16:35:11 +02:00
|
|
|
this.logData = undefined;
|
|
|
|
|
2016-11-08 21:53:08 +01:00
|
|
|
if ( Date.now() < this.allowLargeMediaElementsUntil ) {
|
2017-05-12 16:35:11 +02:00
|
|
|
return 0;
|
2016-11-08 21:53:08 +01:00
|
|
|
}
|
|
|
|
if ( µb.hnSwitches.evaluateZ('no-large-media', this.tabHostname) !== true ) {
|
2017-05-12 16:35:11 +02:00
|
|
|
return 0;
|
2016-11-08 21:53:08 +01:00
|
|
|
}
|
|
|
|
if ( (size >>> 10) < µb.userSettings.largeMediaSize ) {
|
2017-05-12 16:35:11 +02:00
|
|
|
return 0;
|
2016-11-08 21:53:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.largeMediaCount += 1;
|
|
|
|
if ( this.largeMediaTimer === null ) {
|
|
|
|
this.largeMediaTimer = vAPI.setTimeout(
|
|
|
|
this.injectLargeMediaElementScriptlet.bind(this),
|
|
|
|
500
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-05-12 16:35:11 +02:00
|
|
|
if ( µb.logger.isEnabled() ) {
|
|
|
|
this.logData = µb.hnSwitches.toLogData();
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
2016-11-08 21:53:08 +01:00
|
|
|
};
|
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
// https://www.youtube.com/watch?v=drW8p_dTLD4
|
2015-06-10 15:23:48 +02:00
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
/******************************************************************************/
|
2017-05-12 16:35:11 +02:00
|
|
|
|
2017-08-03 16:18:05 +02:00
|
|
|
PageStore.prototype.getBlockedResources = function(request, response) {
|
2017-10-01 13:56:28 +02:00
|
|
|
var µburi = µb.URI,
|
|
|
|
normalURL = µb.normalizePageURL(this.tabId, request.frameURL),
|
|
|
|
frameHostname = µburi.hostnameFromURI(normalURL),
|
|
|
|
resources = request.resources;
|
|
|
|
// Force some resources to go through the filtering engine in order to
|
|
|
|
// populate the blocked-resources cache. This is required because for
|
|
|
|
// some resources it's not possible to detect whether they were blocked
|
|
|
|
// content script-side (i.e. `iframes` -- unlike `img`).
|
2017-08-03 16:18:05 +02:00
|
|
|
if ( Array.isArray(resources) && resources.length !== 0 ) {
|
2017-10-01 13:56:28 +02:00
|
|
|
var context = this.createContextFromFrameHostname(frameHostname);
|
2017-08-03 16:18:05 +02:00
|
|
|
for ( var resource of resources ) {
|
|
|
|
context.requestType = resource.type;
|
2017-10-01 13:56:28 +02:00
|
|
|
context.requestHostname = µburi.hostnameFromURI(resource.url);
|
2017-08-03 16:18:05 +02:00
|
|
|
context.requestURL = resource.url;
|
|
|
|
this.filterRequest(context);
|
2017-05-12 16:35:11 +02:00
|
|
|
}
|
2015-06-10 15:23:48 +02:00
|
|
|
}
|
2017-10-01 13:56:28 +02:00
|
|
|
if ( this.netFilteringCache.hash === response.hash ) { return; }
|
2017-08-03 16:18:05 +02:00
|
|
|
response.hash = this.netFilteringCache.hash;
|
2017-10-01 13:56:28 +02:00
|
|
|
response.blockedResources = this.netFilteringCache.lookupAllBlocked(frameHostname);
|
2015-01-24 18:06:22 +01:00
|
|
|
};
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
return {
|
2014-09-14 22:20:40 +02:00
|
|
|
factory: PageStore.factory
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|