|
|
@ -41,10 +41,10 @@ |
|
|
|
|
|
|
|
var µm = µMatrix; |
|
|
|
|
|
|
|
var recordPageCookiesQueue = {}; |
|
|
|
var removePageCookiesQueue = {}; |
|
|
|
var removeCookieQueue = {}; |
|
|
|
var cookieDict = {}; |
|
|
|
var recordPageCookiesQueue = new Map(); |
|
|
|
var removePageCookiesQueue = new Map(); |
|
|
|
var removeCookieQueue = new Set(); |
|
|
|
var cookieDict = new Map(); |
|
|
|
var cookieEntryJunkyard = []; |
|
|
|
var processRemoveQueuePeriod = 2 * 60 * 1000; |
|
|
|
var processCleanPeriod = 10 * 60 * 1000; |
|
|
@ -54,10 +54,11 @@ var processPageRemoveQueueTimer = null; |
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var CookieEntry = function(cookie) { |
|
|
|
this.set(cookie); |
|
|
|
this.usedOn = new Set(); |
|
|
|
this.init(cookie); |
|
|
|
}; |
|
|
|
|
|
|
|
CookieEntry.prototype.set = function(cookie) { |
|
|
|
CookieEntry.prototype.init = function(cookie) { |
|
|
|
this.secure = cookie.secure; |
|
|
|
this.session = cookie.session; |
|
|
|
this.anySubdomain = cookie.domain.charAt(0) === '.'; |
|
|
@ -67,36 +68,37 @@ CookieEntry.prototype.set = function(cookie) { |
|
|
|
this.name = cookie.name; |
|
|
|
this.value = cookie.value; |
|
|
|
this.tstamp = Date.now(); |
|
|
|
this.usedOn = {}; |
|
|
|
this.usedOn.clear(); |
|
|
|
return this; |
|
|
|
}; |
|
|
|
|
|
|
|
// Release anything which may consume too much memory
|
|
|
|
|
|
|
|
CookieEntry.prototype.unset = function() { |
|
|
|
CookieEntry.prototype.dispose = function() { |
|
|
|
this.hostname = ''; |
|
|
|
this.domain = ''; |
|
|
|
this.path = ''; |
|
|
|
this.name = ''; |
|
|
|
this.value = ''; |
|
|
|
this.usedOn = {}; |
|
|
|
this.usedOn.clear(); |
|
|
|
return this; |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var addCookieToDict = function(cookie) { |
|
|
|
var cookieKey = cookieKeyFromCookie(cookie); |
|
|
|
if ( cookieDict.hasOwnProperty(cookieKey) === false ) { |
|
|
|
var cookieEntry = cookieEntryJunkyard.pop(); |
|
|
|
var cookieKey = cookieKeyFromCookie(cookie), |
|
|
|
cookieEntry = cookieDict.get(cookieKey); |
|
|
|
if ( cookieEntry === undefined ) { |
|
|
|
cookieEntry = cookieEntryJunkyard.pop(); |
|
|
|
if ( cookieEntry ) { |
|
|
|
cookieEntry.set(cookie); |
|
|
|
cookieEntry.init(cookie); |
|
|
|
} else { |
|
|
|
cookieEntry = new CookieEntry(cookie); |
|
|
|
} |
|
|
|
cookieDict[cookieKey] = cookieEntry; |
|
|
|
cookieDict.set(cookieKey, cookieEntry); |
|
|
|
} |
|
|
|
return cookieDict[cookieKey]; |
|
|
|
return cookieEntry; |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
@ -111,13 +113,11 @@ var addCookiesToDict = function(cookies) { |
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var removeCookieFromDict = function(cookieKey) { |
|
|
|
if ( cookieDict.hasOwnProperty(cookieKey) === false ) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
var cookieEntry = cookieDict[cookieKey]; |
|
|
|
delete cookieDict[cookieKey]; |
|
|
|
var cookieEntry = cookieDict.get(cookieKey); |
|
|
|
if ( cookieEntry === undefined ) { return false; } |
|
|
|
cookieDict.delete(cookieKey); |
|
|
|
if ( cookieEntryJunkyard.length < 25 ) { |
|
|
|
cookieEntryJunkyard.push(cookieEntry.unset()); |
|
|
|
cookieEntryJunkyard.push(cookieEntry.dispose()); |
|
|
|
} |
|
|
|
return true; |
|
|
|
}; |
|
|
@ -159,12 +159,6 @@ var cookieKeyFromCookieURL = function(url, type, name) { |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var cookieEntryFromCookie = function(cookie) { |
|
|
|
return cookieDict[cookieKeyFromCookie(cookie)]; |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var cookieURLFromCookieEntry = function(entry) { |
|
|
|
if ( !entry ) { |
|
|
|
return ''; |
|
|
@ -175,10 +169,8 @@ var cookieURLFromCookieEntry = function(entry) { |
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var cookieMatchDomains = function(cookieKey, allHostnamesString) { |
|
|
|
var cookieEntry = cookieDict[cookieKey]; |
|
|
|
if ( !cookieEntry ) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
var cookieEntry = cookieDict.get(cookieKey); |
|
|
|
if ( cookieEntry === undefined ) { return false; } |
|
|
|
if ( allHostnamesString.indexOf(' ' + cookieEntry.hostname + ' ') < 0 ) { |
|
|
|
if ( !cookieEntry.anySubdomain ) { |
|
|
|
return false; |
|
|
@ -202,7 +194,7 @@ var recordPageCookiesAsync = function(pageStats) { |
|
|
|
if ( !pageStats ) { |
|
|
|
return; |
|
|
|
} |
|
|
|
recordPageCookiesQueue[pageStats.pageUrl] = pageStats; |
|
|
|
recordPageCookiesQueue.set(pageStats.pageUrl, pageStats); |
|
|
|
if ( processPageRecordQueueTimer === null ) { |
|
|
|
processPageRecordQueueTimer = vAPI.setTimeout(processPageRecordQueue, 1000); |
|
|
|
} |
|
|
@ -220,11 +212,9 @@ var cookieLogEntryBuilder = [ |
|
|
|
]; |
|
|
|
|
|
|
|
var recordPageCookie = function(pageStore, cookieKey) { |
|
|
|
if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { return; } |
|
|
|
|
|
|
|
var cookieEntry = cookieDict[cookieKey]; |
|
|
|
var cookieEntry = cookieDict.get(cookieKey); |
|
|
|
var pageHostname = pageStore.pageHostname; |
|
|
|
var block = µm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie'); |
|
|
|
|
|
|
@ -240,7 +230,7 @@ var recordPageCookie = function(pageStore, cookieKey) { |
|
|
|
pageStore.recordRequest('cookie', cookieURL, block); |
|
|
|
µm.logger.writeOne(pageStore.tabId, 'net', pageHostname, cookieURL, 'cookie', block); |
|
|
|
|
|
|
|
cookieEntry.usedOn[pageHostname] = true; |
|
|
|
cookieEntry.usedOn.add(pageHostname); |
|
|
|
|
|
|
|
// rhill 2013-11-21:
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/65
|
|
|
@ -267,7 +257,7 @@ var removePageCookiesAsync = function(pageStats) { |
|
|
|
if ( !pageStats ) { |
|
|
|
return; |
|
|
|
} |
|
|
|
removePageCookiesQueue[pageStats.pageUrl] = pageStats; |
|
|
|
removePageCookiesQueue.set(pageStats.pageUrl, pageStats); |
|
|
|
if ( processPageRemoveQueueTimer === null ) { |
|
|
|
processPageRemoveQueueTimer = vAPI.setTimeout(processPageRemoveQueue, 15 * 1000); |
|
|
|
} |
|
|
@ -278,7 +268,7 @@ var removePageCookiesAsync = function(pageStats) { |
|
|
|
// Candidate for removal
|
|
|
|
|
|
|
|
var removeCookieAsync = function(cookieKey) { |
|
|
|
removeCookieQueue[cookieKey] = true; |
|
|
|
removeCookieQueue.add(cookieKey); |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
@ -318,13 +308,10 @@ var i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError'); |
|
|
|
var processPageRecordQueue = function() { |
|
|
|
processPageRecordQueueTimer = null; |
|
|
|
|
|
|
|
for ( var pageURL in recordPageCookiesQueue ) { |
|
|
|
if ( !recordPageCookiesQueue.hasOwnProperty(pageURL) ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
findAndRecordPageCookies(recordPageCookiesQueue[pageURL]); |
|
|
|
delete recordPageCookiesQueue[pageURL]; |
|
|
|
for ( var pageStore of recordPageCookiesQueue.values() ) { |
|
|
|
findAndRecordPageCookies(pageStore); |
|
|
|
} |
|
|
|
recordPageCookiesQueue.clear(); |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
@ -332,13 +319,10 @@ var processPageRecordQueue = function() { |
|
|
|
var processPageRemoveQueue = function() { |
|
|
|
processPageRemoveQueueTimer = null; |
|
|
|
|
|
|
|
for ( var pageURL in removePageCookiesQueue ) { |
|
|
|
if ( !removePageCookiesQueue.hasOwnProperty(pageURL) ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
findAndRemovePageCookies(removePageCookiesQueue[pageURL]); |
|
|
|
delete removePageCookiesQueue[pageURL]; |
|
|
|
for ( var pageStore of removePageCookiesQueue.values() ) { |
|
|
|
findAndRemovePageCookies(pageStore); |
|
|
|
} |
|
|
|
removePageCookiesQueue.clear(); |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
@ -359,19 +343,12 @@ var processRemoveQueue = function() { |
|
|
|
var srcHostnames; |
|
|
|
var cookieEntry; |
|
|
|
|
|
|
|
for ( var cookieKey in removeCookieQueue ) { |
|
|
|
if ( removeCookieQueue.hasOwnProperty(cookieKey) === false ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
delete removeCookieQueue[cookieKey]; |
|
|
|
|
|
|
|
for ( var cookieKey of removeCookieQueue ) { |
|
|
|
// rhill 2014-05-12: Apparently this can happen. I have to
|
|
|
|
// investigate how (A session cookie has same name as a
|
|
|
|
// persistent cookie?)
|
|
|
|
cookieEntry = cookieDict[cookieKey]; |
|
|
|
if ( !cookieEntry ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
cookieEntry = cookieDict.get(cookieKey); |
|
|
|
if ( cookieEntry === undefined ) { continue; } |
|
|
|
|
|
|
|
// Delete obsolete session cookies: enabled.
|
|
|
|
if ( tstampObsolete !== 0 && cookieEntry.session ) { |
|
|
@ -399,6 +376,8 @@ var processRemoveQueue = function() { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
removeCookieQueue.clear(); |
|
|
|
|
|
|
|
vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod); |
|
|
|
}; |
|
|
|
|
|
|
@ -407,16 +386,29 @@ var processRemoveQueue = function() { |
|
|
|
// Once in a while, we go ahead and clean everything that might have been
|
|
|
|
// left behind.
|
|
|
|
|
|
|
|
// Remove only some of the cookies which are candidate for removal: who knows,
|
|
|
|
// maybe a user has 1000s of cookies sitting in his browser...
|
|
|
|
|
|
|
|
var processClean = function() { |
|
|
|
// Remove only some of the cookies which are candidate for removal:
|
|
|
|
// who knows, maybe a user has 1000s of cookies sitting in his
|
|
|
|
// browser...
|
|
|
|
var cookieKeys = Object.keys(cookieDict); |
|
|
|
if ( cookieKeys.length > 25 ) { |
|
|
|
cookieKeys = cookieKeys.sort(function(){return Math.random() < 0.5;}).splice(0, 50); |
|
|
|
} |
|
|
|
while ( cookieKeys.length ) { |
|
|
|
removeCookieAsync(cookieKeys.pop()); |
|
|
|
var us = µm.userSettings; |
|
|
|
if ( us.deleteCookies || us.deleteUnusedSessionCookies ) { |
|
|
|
var cookieKeys = Array.from(cookieDict.keys()), |
|
|
|
len = cookieKeys.length, |
|
|
|
step, offset, n; |
|
|
|
if ( len > 25 ) { |
|
|
|
step = len / 25; |
|
|
|
offset = Math.floor(Math.random() * len); |
|
|
|
n = 25; |
|
|
|
} else { |
|
|
|
step = 1; |
|
|
|
offset = 0; |
|
|
|
n = len; |
|
|
|
} |
|
|
|
var i = offset; |
|
|
|
while ( n-- ) { |
|
|
|
removeCookieAsync(cookieKeys[Math.floor(i % len)]); |
|
|
|
i += step; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
vAPI.setTimeout(processClean, processCleanPeriod); |
|
|
@ -425,45 +417,33 @@ var processClean = function() { |
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var findAndRecordPageCookies = function(pageStore) { |
|
|
|
for ( var cookieKey in cookieDict ) { |
|
|
|
if ( !cookieDict.hasOwnProperty(cookieKey) ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) === false ) { |
|
|
|
continue; |
|
|
|
for ( var cookieKey of cookieDict.keys() ) { |
|
|
|
if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) { |
|
|
|
recordPageCookie(pageStore, cookieKey); |
|
|
|
} |
|
|
|
recordPageCookie(pageStore, cookieKey); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var findAndRemovePageCookies = function(pageStore) { |
|
|
|
for ( var cookieKey in cookieDict ) { |
|
|
|
if ( !cookieDict.hasOwnProperty(cookieKey) ) { |
|
|
|
continue; |
|
|
|
for ( var cookieKey of cookieDict.keys() ) { |
|
|
|
if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) { |
|
|
|
removeCookieAsync(cookieKey); |
|
|
|
} |
|
|
|
if ( !cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
removeCookieAsync(cookieKey); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/******************************************************************************/ |
|
|
|
|
|
|
|
var canRemoveCookie = function(cookieKey, srcHostnames) { |
|
|
|
var cookieEntry = cookieDict[cookieKey]; |
|
|
|
if ( !cookieEntry ) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
var cookieEntry = cookieDict.get(cookieKey); |
|
|
|
if ( cookieEntry === undefined ) { return false; } |
|
|
|
|
|
|
|
var cookieHostname = cookieEntry.hostname; |
|
|
|
var srcHostname; |
|
|
|
|
|
|
|
for ( srcHostname in cookieEntry.usedOn ) { |
|
|
|
if ( cookieEntry.usedOn.hasOwnProperty(srcHostname) === false ) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
for ( srcHostname of cookieEntry.usedOn ) { |
|
|
|
if ( µm.mustAllow(srcHostname, cookieHostname, 'cookie') ) { |
|
|
|
return false; |
|
|
|
} |
|
|
@ -500,14 +480,12 @@ vAPI.cookies.onChanged = function(cookie) { |
|
|
|
// rhill 2013-12-11: If cookie value didn't change, no need to record.
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/79
|
|
|
|
var cookieKey = cookieKeyFromCookie(cookie); |
|
|
|
var cookieEntry = cookieDict[cookieKey]; |
|
|
|
if ( !cookieEntry ) { |
|
|
|
var cookieEntry = cookieDict.get(cookieKey); |
|
|
|
if ( cookieEntry === undefined ) { |
|
|
|
cookieEntry = addCookieToDict(cookie); |
|
|
|
} else { |
|
|
|
cookieEntry.tstamp = Date.now(); |
|
|
|
if ( cookie.value === cookieEntry.value ) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if ( cookie.value === cookieEntry.value ) { return; } |
|
|
|
cookieEntry.value = cookie.value; |
|
|
|
} |
|
|
|
|
|
|
@ -543,8 +521,8 @@ vAPI.cookies.onRemoved = function(cookie) { |
|
|
|
// Listen to any change in cookieland, we will update page stats accordingly.
|
|
|
|
|
|
|
|
vAPI.cookies.onAllRemoved = function() { |
|
|
|
for ( var cookieKey in cookieDict ) { |
|
|
|
if ( cookieDict.hasOwnProperty(cookieKey) && removeCookieFromDict(cookieKey) ) { |
|
|
|
for ( var cookieKey of cookieDict.keys() ) { |
|
|
|
if ( removeCookieFromDict(cookieKey) ) { |
|
|
|
µm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey)); |
|
|
|
} |
|
|
|
} |
|
|
|