uBlock/src/js/filtering-context.js
2019-03-15 07:47:36 -04:00

296 lines
10 KiB
JavaScript

/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2018-present Raymond Hill
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
*/
'use strict';
/******************************************************************************/
µBlock.FilteringContext = function(other) {
if ( other instanceof µBlock.FilteringContext ) {
return this.fromFilteringContext(other);
}
this.tstamp = 0;
this.realm = '';
this.type = undefined;
this.url = undefined;
this.hostname = undefined;
this.domain = undefined;
this.docId = undefined;
this.docOrigin = undefined;
this.docHostname = undefined;
this.docDomain = undefined;
this.tabId = undefined;
this.tabOrigin = undefined;
this.tabHostname = undefined;
this.tabDomain = undefined;
this.filter = undefined;
};
µBlock.FilteringContext.prototype = {
fromTabId: function(tabId) {
const tabContext = µBlock.tabContextManager.mustLookup(tabId);
this.tabOrigin = tabContext.origin;
this.tabHostname = tabContext.rootHostname;
this.tabDomain = tabContext.rootDomain;
this.tabId = tabContext.tabId;
return this;
},
// https://github.com/uBlockOrigin/uBlock-issues/issues/459
// In case of a request for frame and if ever no context is specified,
// assume the origin of the context is the same as the request itself.
fromWebrequestDetails: function(details) {
const tabId = details.tabId;
if ( tabId > 0 && details.type === 'main_frame' ) {
µBlock.tabContextManager.push(tabId, details.url);
}
this.fromTabId(tabId);
this.realm = '';
this.type = details.type;
this.setURL(details.url);
this.docId = details.type !== 'sub_frame'
? details.frameId
: details.parentFrameId;
if ( this.tabId > 0 ) {
if ( this.docId === 0 ) {
this.docOrigin = this.tabOrigin;
this.docHostname = this.tabHostname;
this.docDomain = this.tabDomain;
} else if ( details.documentUrl !== undefined ) {
this.setDocOriginFromURL(details.documentUrl);
} else {
const pageStore = µBlock.pageStoreFromTabId(this.tabId);
const docStore = pageStore && pageStore.frames.get(this.docId);
if ( docStore ) {
this.docOrigin = undefined;
this.docHostname = docStore.pageHostname;
this.docDomain = docStore.pageDomain;
} else {
this.setDocOrigin(this.tabOrigin);
}
}
} else if ( details.documentUrl !== undefined ) {
const origin = this.originFromURI(
µBlock.normalizePageURL(0, details.documentUrl)
);
this.setDocOrigin(origin).setTabOrigin(origin);
} else if ( this.type === 'sub_frame' ) {
const origin = this.originFromURI(this.url);
this.setDocOrigin(origin).setTabOrigin(origin);
} else {
this.setDocOrigin(this.tabOrigin);
}
this.filter = undefined;
return this;
},
fromFilteringContext: function(other) {
this.realm = other.realm;
this.type = other.type;
this.url = other.url;
this.hostname = other.hostname;
this.domain = other.domain;
this.docId = other.docId;
this.docOrigin = other.docOrigin;
this.docHostname = other.docHostname;
this.docDomain = other.docDomain;
this.tabId = other.tabId;
this.tabOrigin = other.tabOrigin;
this.tabHostname = other.tabHostname;
this.tabDomain = other.tabDomain;
this.filter = undefined;
return this;
},
duplicate: function() {
return (new µBlock.FilteringContext(this));
},
setRealm: function(a) {
this.realm = a;
return this;
},
setType: function(a) {
this.type = a;
return this;
},
setURL: function(a) {
if ( a !== this.url ) {
this.hostname = this.domain = undefined;
this.url = a;
}
return this;
},
getHostname: function() {
if ( this.hostname === undefined ) {
this.hostname = this.hostnameFromURI(this.url);
}
return this.hostname;
},
setHostname: function(a) {
if ( a !== this.hostname ) {
this.domain = undefined;
this.hostname = a;
}
return this;
},
getDomain: function() {
if ( this.domain === undefined ) {
this.domain = this.domainFromHostname(this.getHostname());
}
return this.domain;
},
setDomain: function(a) {
this.domain = a;
return this;
},
getDocOrigin: function() {
if ( this.docOrigin === undefined ) {
this.docOrigin = this.tabOrigin;
}
return this.docOrigin;
},
setDocOrigin: function(a) {
if ( a !== this.docOrigin ) {
this.docHostname = this.docDomain = undefined;
this.docOrigin = a;
}
return this;
},
setDocOriginFromURL: function(a) {
return this.setDocOrigin(this.originFromURI(a));
},
getDocHostname: function() {
if ( this.docHostname === undefined ) {
this.docHostname = this.hostnameFromURI(this.getDocOrigin());
}
return this.docHostname;
},
setDocHostname: function(a) {
if ( a !== this.docHostname ) {
this.docDomain = undefined;
this.docHostname = a;
}
return this;
},
getDocDomain: function() {
if ( this.docDomain === undefined ) {
this.docDomain = this.domainFromHostname(this.getDocHostname());
}
return this.docDomain;
},
setDocDomain: function(a) {
this.docDomain = a;
return this;
},
// The idea is to minimize the amout of work done to figure out whether
// the resource is 3rd-party to the document.
is3rdPartyToDoc: function() {
let docDomain = this.getDocDomain();
if ( docDomain === '' ) { docDomain = this.docHostname; }
if ( this.domain !== undefined && this.domain !== '' ) {
return this.domain !== docDomain;
}
const hostname = this.getHostname();
if ( hostname.endsWith(docDomain) === false ) { return true; }
const i = hostname.length - docDomain.length;
if ( i === 0 ) { return false; }
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
},
setTabId: function(a) {
this.tabId = a;
return this;
},
getTabOrigin: function() {
if ( this.tabOrigin === undefined ) {
const tabContext = µBlock.tabContextManager.mustLookup(this.tabId);
this.tabOrigin = tabContext.origin;
this.tabHostname = tabContext.rootHostname;
this.tabDomain = tabContext.rootDomain;
}
return this.tabOrigin;
},
setTabOrigin: function(a) {
if ( a !== this.tabOrigin ) {
this.tabHostname = this.tabDomain = undefined;
this.tabOrigin = a;
}
return this;
},
setTabOriginFromURL: function(a) {
return this.setTabOrigin(this.originFromURI(a));
},
getTabHostname: function() {
if ( this.tabHostname === undefined ) {
this.tabHostname = this.hostnameFromURI(this.getTabOrigin());
}
return this.tabHostname;
},
setTabHostname: function(a) {
if ( a !== this.tabHostname ) {
this.tabDomain = undefined;
this.tabHostname = a;
}
return this;
},
getTabDomain: function() {
if ( this.tabDomain === undefined ) {
this.tabDomain = this.domainFromHostname(this.getTabHostname());
}
return this.tabDomain;
},
setTabDomain: function(a) {
this.docDomain = a;
return this;
},
// The idea is to minimize the amout of work done to figure out whether
// the resource is 3rd-party to the top document.
is3rdPartyToTab: function() {
let tabDomain = this.getTabDomain();
if ( tabDomain === '' ) { tabDomain = this.tabHostname; }
if ( this.domain !== undefined && this.domain !== '' ) {
return this.domain !== tabDomain;
}
const hostname = this.getHostname();
if ( hostname.endsWith(tabDomain) === false ) { return true; }
const i = hostname.length - tabDomain.length;
if ( i === 0 ) { return false; }
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
},
setFilter: function(a) {
this.filter = a;
return this;
},
toLogger: function() {
this.tstamp = Date.now();
if ( this.domain === undefined ) {
void this.getDomain();
}
if ( this.docDomain === undefined ) {
void this.getDocDomain();
}
if ( this.tabDomain === undefined ) {
void this.getTabDomain();
}
µBlock.logger.writeOne(this);
},
originFromURI: µBlock.URI.originFromURI,
hostnameFromURI: µBlock.URI.hostnameFromURI,
domainFromHostname: µBlock.URI.domainFromHostname,
};
µBlock.filteringContext = new µBlock.FilteringContext();