mirror of
https://github.com/gorhill/uMatrix.git
synced 2024-06-14 16:25:03 +12:00
parent
72c9429d74
commit
6eaa85eee1
File diff suppressed because it is too large
Load diff
|
@ -129,11 +129,7 @@ var onTabsReady = function(tabs) {
|
|||
// console.debug('start.js > binding %d tabs', i);
|
||||
while ( i-- ) {
|
||||
tab = tabs[i];
|
||||
µm.tabContextManager.commit(tab.id, tab.url);
|
||||
// https://github.com/gorhill/uMatrix/issues/56
|
||||
// We must unbind first to flush out potentially bad domain names.
|
||||
µm.unbindTabFromPageStats(tab.id);
|
||||
µm.bindTabToPageStats(tab.id);
|
||||
µm.tabContextManager.push(tab.id, tab.url, 'newURL');
|
||||
}
|
||||
|
||||
onAllDone();
|
||||
|
|
262
src/js/tab.js
262
src/js/tab.js
|
@ -150,7 +150,15 @@ housekeep itself.
|
|||
var mostRecentRootDocURL = '';
|
||||
var mostRecentRootDocURLTimestamp = 0;
|
||||
|
||||
var gcPeriod = 10 * 60 * 1000;
|
||||
var gcPeriod = 31 * 60 * 1000; // every 31 minutes
|
||||
|
||||
// A pushed entry is removed from the stack unless it is committed with
|
||||
// a set time.
|
||||
var StackEntry = function(url, commit) {
|
||||
this.url = url;
|
||||
this.committed = commit;
|
||||
this.tstamp = Date.now();
|
||||
};
|
||||
|
||||
var TabContext = function(tabId) {
|
||||
this.tabId = tabId;
|
||||
|
@ -161,9 +169,8 @@ housekeep itself.
|
|||
this.rootHostname =
|
||||
this.rootDomain = '';
|
||||
this.secure = false;
|
||||
this.timer = null;
|
||||
this.onTabCallback = null;
|
||||
this.onTimerCallback = null;
|
||||
this.commitTimer = null;
|
||||
this.gcTimer = null;
|
||||
|
||||
tabContexts[tabId] = this;
|
||||
};
|
||||
|
@ -172,119 +179,122 @@ housekeep itself.
|
|||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
}
|
||||
if ( this.timer !== null ) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
if ( this.gcTimer !== null ) {
|
||||
clearTimeout(this.gcTimer);
|
||||
this.gcTimer = null;
|
||||
}
|
||||
delete tabContexts[this.tabId];
|
||||
};
|
||||
|
||||
TabContext.prototype.onTab = function(tab) {
|
||||
if ( tab ) {
|
||||
this.timer = vAPI.setTimeout(this.onTimerCallback, gcPeriod);
|
||||
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
|
||||
} else {
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
TabContext.prototype.onTimer = function() {
|
||||
this.timer = null;
|
||||
TabContext.prototype.onGC = function() {
|
||||
this.gcTimer = null;
|
||||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
}
|
||||
vAPI.tabs.get(this.tabId, this.onTabCallback);
|
||||
vAPI.tabs.get(this.tabId, this.onTab.bind(this));
|
||||
};
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/248
|
||||
// Stack entries have to be committed to stick. Non-committed stack
|
||||
// entries are removed after a set delay.
|
||||
TabContext.prototype.onCommit = function() {
|
||||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
}
|
||||
this.commitTimer = null;
|
||||
// Remove uncommitted entries at the top of the stack.
|
||||
var i = this.stack.length;
|
||||
while ( i-- ) {
|
||||
if ( this.stack[i].committed ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// https://github.com/gorhill/uBlock/issues/300
|
||||
// If no committed entry was found, fall back on the bottom-most one
|
||||
// as being the committed one by default.
|
||||
if ( i === -1 && this.stack.length !== 0 ) {
|
||||
this.stack[0].committed = true;
|
||||
i = 0;
|
||||
}
|
||||
i += 1;
|
||||
if ( i < this.stack.length ) {
|
||||
this.stack.length = i;
|
||||
this.update();
|
||||
µm.bindTabToPageStats(this.tabId, 'newURL');
|
||||
}
|
||||
};
|
||||
|
||||
// This takes care of orphanized tab contexts. Can't be started for all
|
||||
// contexts, as the behind-the-scene context is permanent -- so we do not
|
||||
// want to slush it.
|
||||
// want to flush it.
|
||||
TabContext.prototype.autodestroy = function() {
|
||||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
}
|
||||
this.onTabCallback = this.onTab.bind(this);
|
||||
this.onTimerCallback = this.onTimer.bind(this);
|
||||
this.timer = vAPI.setTimeout(this.onTimerCallback, gcPeriod);
|
||||
this.gcTimer = vAPI.setTimeout(this.onGC.bind(this), gcPeriod);
|
||||
};
|
||||
|
||||
// Update just force all properties to be updated to match the most current
|
||||
// Update just force all properties to be updated to match the most recent
|
||||
// root URL.
|
||||
TabContext.prototype.update = function() {
|
||||
if ( this.stack.length === 0 ) {
|
||||
this.rawURL =
|
||||
this.normalURL =
|
||||
this.scheme =
|
||||
this.rootHostname =
|
||||
this.rootDomain = '';
|
||||
} else {
|
||||
this.rawURL = this.stack[this.stack.length - 1];
|
||||
this.normalURL = µm.normalizePageURL(this.tabId, this.rawURL);
|
||||
this.scheme = µm.URI.schemeFromURI(this.rawURL);
|
||||
this.rootHostname = µm.URI.hostnameFromURI(this.normalURL);
|
||||
this.rootDomain = µm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
|
||||
this.rawURL = this.normalURL = this.scheme =
|
||||
this.rootHostname = this.rootDomain = '';
|
||||
this.secure = false;
|
||||
return;
|
||||
}
|
||||
this.rawURL = this.stack[this.stack.length - 1].url;
|
||||
this.normalURL = µm.normalizePageURL(this.tabId, this.rawURL);
|
||||
this.scheme = µm.URI.schemeFromURI(this.rawURL);
|
||||
this.rootHostname = µm.URI.hostnameFromURI(this.normalURL);
|
||||
this.rootDomain = µm.URI.domainFromHostname(this.rootHostname) || this.rootHostname;
|
||||
this.secure = µm.URI.isSecureScheme(this.scheme);
|
||||
};
|
||||
|
||||
// Called whenever a candidate root URL is spotted for the tab.
|
||||
TabContext.prototype.push = function(url) {
|
||||
TabContext.prototype.push = function(url, context) {
|
||||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
}
|
||||
var committed = context !== undefined;
|
||||
var count = this.stack.length;
|
||||
if ( count !== 0 && this.stack[count - 1] === url ) {
|
||||
var topEntry = this.stack[count - 1];
|
||||
if ( topEntry && topEntry.url === url ) {
|
||||
if ( committed ) {
|
||||
topEntry.committed = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.stack.push(url);
|
||||
this.update();
|
||||
};
|
||||
|
||||
// Called when a former push is a false positive:
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/516
|
||||
TabContext.prototype.unpush = function(url) {
|
||||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
if ( this.commitTimer !== null ) {
|
||||
clearTimeout(this.commitTimer);
|
||||
}
|
||||
// We are not going to unpush if there is no other candidate, the
|
||||
// point of unpush is to make space for a better candidate.
|
||||
if ( this.stack.length === 1 ) {
|
||||
return;
|
||||
}
|
||||
var pos = this.stack.indexOf(url);
|
||||
if ( pos === -1 ) {
|
||||
return;
|
||||
}
|
||||
this.stack.splice(pos, 1);
|
||||
if ( this.stack.length === 0 ) {
|
||||
this.destroy();
|
||||
return;
|
||||
}
|
||||
if ( pos !== this.stack.length ) {
|
||||
return;
|
||||
if ( committed ) {
|
||||
this.stack = [new StackEntry(url, true)];
|
||||
} else {
|
||||
this.stack.push(new StackEntry(url));
|
||||
this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
|
||||
}
|
||||
this.update();
|
||||
};
|
||||
|
||||
// This tells that the url is definitely the one to be associated with the
|
||||
// tab, there is no longer any ambiguity about which root URL is really
|
||||
// sitting in which tab.
|
||||
TabContext.prototype.commit = function(url) {
|
||||
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||
return;
|
||||
}
|
||||
this.stack = [url];
|
||||
this.update();
|
||||
µm.bindTabToPageStats(this.tabId, context);
|
||||
};
|
||||
|
||||
// These are to be used for the API of the tab context manager.
|
||||
|
||||
var push = function(tabId, url) {
|
||||
var push = function(tabId, url, context) {
|
||||
var entry = tabContexts[tabId];
|
||||
if ( entry === undefined ) {
|
||||
entry = new TabContext(tabId);
|
||||
entry.autodestroy();
|
||||
}
|
||||
entry.push(url);
|
||||
entry.push(url, context);
|
||||
mostRecentRootDocURL = url;
|
||||
mostRecentRootDocURLTimestamp = Date.now();
|
||||
return entry;
|
||||
|
@ -326,23 +336,6 @@ housekeep itself.
|
|||
return tabContexts[vAPI.noTabId];
|
||||
};
|
||||
|
||||
var commit = function(tabId, url) {
|
||||
var entry = tabContexts[tabId];
|
||||
if ( entry === undefined ) {
|
||||
entry = push(tabId, url);
|
||||
} else {
|
||||
entry.commit(url);
|
||||
}
|
||||
return entry;
|
||||
};
|
||||
|
||||
var unpush = function(tabId, url) {
|
||||
var entry = tabContexts[tabId];
|
||||
if ( entry !== undefined ) {
|
||||
entry.unpush(url);
|
||||
}
|
||||
};
|
||||
|
||||
var lookup = function(tabId) {
|
||||
return tabContexts[tabId] || null;
|
||||
};
|
||||
|
@ -350,88 +343,48 @@ housekeep itself.
|
|||
// Behind-the-scene tab context
|
||||
(function() {
|
||||
var entry = new TabContext(vAPI.noTabId);
|
||||
entry.stack.push('');
|
||||
entry.stack.push(new StackEntry('', true));
|
||||
entry.rawURL = '';
|
||||
entry.normalURL = µm.normalizePageURL(entry.tabId);
|
||||
entry.rootHostname = µm.URI.hostnameFromURI(entry.normalURL);
|
||||
entry.rootDomain = µm.URI.domainFromHostname(entry.rootHostname) || entry.rootHostname;
|
||||
})();
|
||||
|
||||
// Context object, typically to be used to feed filtering engines.
|
||||
var Context = function(tabId) {
|
||||
var tabContext = lookup(tabId);
|
||||
this.rootHostname = tabContext.rootHostname;
|
||||
this.rootDomain = tabContext.rootDomain;
|
||||
this.pageHostname =
|
||||
this.pageDomain =
|
||||
this.requestURL =
|
||||
this.requestHostname =
|
||||
this.requestDomain = '';
|
||||
vAPI.tabs.onNavigation = function(details) {
|
||||
var tabId = details.tabId;
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||
return;
|
||||
}
|
||||
push(tabId, details.url, 'newURL');
|
||||
};
|
||||
|
||||
var createContext = function(tabId) {
|
||||
return new Context(tabId);
|
||||
vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
|
||||
if ( typeof tab.url !== 'string' || tab.url === '' ) {
|
||||
return;
|
||||
}
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||
return;
|
||||
}
|
||||
if ( changeInfo.url ) {
|
||||
push(tabId, changeInfo.url, 'updateURL');
|
||||
}
|
||||
};
|
||||
|
||||
vAPI.tabs.onClosed = function(tabId) {
|
||||
µm.unbindTabFromPageStats(tabId);
|
||||
var entry = tabContexts[tabId];
|
||||
if ( entry instanceof TabContext ) {
|
||||
entry.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
push: push,
|
||||
unpush: unpush,
|
||||
commit: commit,
|
||||
lookup: lookup,
|
||||
mustLookup: mustLookup,
|
||||
createContext: createContext
|
||||
mustLookup: mustLookup
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// When the DOM content of root frame is loaded, this means the tab
|
||||
// content has changed.
|
||||
|
||||
vAPI.tabs.onNavigation = function(details) {
|
||||
// This actually can happen
|
||||
var tabId = details.tabId;
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//console.log('vAPI.tabs.onNavigation: %s %s %o', details.url, details.transitionType, details.transitionQualifiers);
|
||||
|
||||
µm.tabContextManager.commit(tabId, details.url);
|
||||
µm.bindTabToPageStats(tabId, 'commit');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// It may happen the URL in the tab changes, while the page's document
|
||||
// stays the same (for instance, Google Maps). Without this listener,
|
||||
// the extension icon won't be properly refreshed.
|
||||
|
||||
vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
|
||||
if ( !tab.url || tab.url === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This actually can happen
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( changeInfo.url ) {
|
||||
µm.tabContextManager.commit(tabId, changeInfo.url);
|
||||
µm.bindTabToPageStats(tabId, 'updated');
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.tabs.onClosed = function(tabId) {
|
||||
µm.unbindTabFromPageStats(tabId);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.tabs.registerListeners();
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -466,19 +419,13 @@ vAPI.tabs.registerListeners();
|
|||
return pageStore;
|
||||
}
|
||||
|
||||
// Do not change anything if it's weak binding -- typically when
|
||||
// binding from network request handler.
|
||||
if ( context === 'weak' ) {
|
||||
return pageStore;
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uMatrix/issues/37
|
||||
// Just rebind whenever possible: the URL changed, but the document
|
||||
// maybe is the same.
|
||||
// Example: Google Maps, Github
|
||||
// https://github.com/gorhill/uMatrix/issues/72
|
||||
// Need to double-check that the new scope is same as old scope
|
||||
if ( context === 'updated' && pageStore.pageHostname === tabContext.rootHostname ) {
|
||||
if ( context === 'updateURL' && pageStore.pageHostname === tabContext.rootHostname ) {
|
||||
pageStore.rawURL = tabContext.rawURL;
|
||||
pageStore.normalURL = normalURL;
|
||||
this.updateTitle(tabId);
|
||||
|
@ -516,6 +463,7 @@ vAPI.tabs.registerListeners();
|
|||
if ( pageStore === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete this.pageStores[tabId];
|
||||
this.pageStoresToken = Date.now();
|
||||
|
||||
|
|
|
@ -43,11 +43,11 @@ var onBeforeRootFrameRequestHandler = function(details) {
|
|||
|
||||
var tabContext = µm.tabContextManager.mustLookup(tabId);
|
||||
var rootHostname = tabContext.rootHostname;
|
||||
var pageStore = µm.bindTabToPageStats(tabId);
|
||||
|
||||
// Disallow request as per matrix?
|
||||
var block = µm.mustBlock(rootHostname, details.hostname, 'doc');
|
||||
|
||||
var pageStore = µm.pageStoreFromTabId(tabId);
|
||||
pageStore.recordRequest('doc', requestURL, block);
|
||||
µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block);
|
||||
|
||||
|
@ -307,11 +307,7 @@ var onHeadersReceived = function(details) {
|
|||
// https://github.com/gorhill/uMatrix/issues/145
|
||||
// Check if the main_frame is a download
|
||||
if ( requestType === 'doc' ) {
|
||||
if ( headerValue(details.responseHeaders, 'content-type').lastIndexOf('application/x-', 0) === 0 ) {
|
||||
µm.tabContextManager.unpush(tabId, requestURL);
|
||||
} else {
|
||||
µm.tabContextManager.push(tabId, requestURL);
|
||||
}
|
||||
µm.tabContextManager.push(tabId, requestURL);
|
||||
}
|
||||
|
||||
var tabContext = µm.tabContextManager.lookup(tabId);
|
||||
|
|
Loading…
Reference in a new issue