aboutsummaryrefslogtreecommitdiffstats
path: root/js/cookies.js
diff options
context:
space:
mode:
authorJesús <heckyel@hyperbola.info>2019-12-30 15:55:13 -0500
committerJesús <heckyel@hyperbola.info>2019-12-30 15:55:13 -0500
commit288df6a7bf8b933e2dc499e38f4915fcf974c14b (patch)
tree77bba994f260c064d3ee7f76c427ddfaa4f91710 /js/cookies.js
parenta2c9deaa145b780722e93b3899600f287c8094a4 (diff)
downloadematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.tar.lz
ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.tar.xz
ematrix-288df6a7bf8b933e2dc499e38f4915fcf974c14b.zip
backport
- Flush caches on upgrade - Properly handle FrameModule's unloading - Use the new module and remove the old implementation
Diffstat (limited to 'js/cookies.js')
-rw-r--r--js/cookies.js464
1 files changed, 163 insertions, 301 deletions
diff --git a/js/cookies.js b/js/cookies.js
index ee271ca..2e15e03 100644
--- a/js/cookies.js
+++ b/js/cookies.js
@@ -28,181 +28,43 @@
"use strict";
-/******************************************************************************/
-
// Isolate from global namespace
// Use cached-context approach rather than object-based approach, as details
// of the implementation do not need to be visible
-ηMatrix.cookieHunter = (function() {
-
- /******************************************************************************/
-
- var ηm = ηMatrix;
+ηMatrix.cookieHunter = (function () {
+ Cu.import('chrome://ematrix/content/lib/UriTools.jsm');
+ Cu.import('chrome://ematrix/content/lib/CookieCache.jsm');
- 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;
- var processPageRecordQueueTimer = null;
- var processPageRemoveQueueTimer = null;
-
- /******************************************************************************/
-
- var CookieEntry = function(cookie) {
- this.usedOn = new Set();
- this.init(cookie);
- };
-
- CookieEntry.prototype.init = function(cookie) {
- this.secure = cookie.secure;
- this.session = cookie.session;
- this.anySubdomain = cookie.domain.charAt(0) === '.';
- this.hostname = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
- this.domain = ηm.URI.domainFromHostname(this.hostname) || this.hostname;
- this.path = cookie.path;
- this.name = cookie.name;
- this.value = cookie.value;
- this.tstamp = Date.now();
- this.usedOn.clear();
- return this;
- };
-
- // Release anything which may consume too much memory
-
- CookieEntry.prototype.dispose = function() {
- this.hostname = '';
- this.domain = '';
- this.path = '';
- this.name = '';
- this.value = '';
- this.usedOn.clear();
- return this;
- };
-
- /******************************************************************************/
-
- var addCookieToDict = function(cookie) {
- var cookieKey = cookieKeyFromCookie(cookie),
- cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) {
- cookieEntry = cookieEntryJunkyard.pop();
- if ( cookieEntry ) {
- cookieEntry.init(cookie);
- } else {
- cookieEntry = new CookieEntry(cookie);
- }
- cookieDict.set(cookieKey, cookieEntry);
- }
- return cookieEntry;
- };
-
- /******************************************************************************/
-
- var addCookiesToDict = function(cookies) {
- var i = cookies.length;
- while ( i-- ) {
- addCookieToDict(cookies[i]);
- }
- };
+ let ηm = ηMatrix;
- /******************************************************************************/
-
- var removeCookieFromDict = function(cookieKey) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
- cookieDict.delete(cookieKey);
- if ( cookieEntryJunkyard.length < 25 ) {
- cookieEntryJunkyard.push(cookieEntry.dispose());
- }
- return true;
- };
-
- /******************************************************************************/
-
- var cookieKeyBuilder = [
- '', // 0 = scheme
- '://',
- '', // 2 = domain
- '', // 3 = path
- '{',
- '', // 5 = persistent or session
- '-cookie:',
- '', // 7 = name
- '}'
- ];
-
- var cookieKeyFromCookie = function(cookie) {
- var cb = cookieKeyBuilder;
- cb[0] = cookie.secure ? 'https' : 'http';
- cb[2] = cookie.domain.charAt(0) === '.' ? cookie.domain.slice(1) : cookie.domain;
- cb[3] = cookie.path;
- cb[5] = cookie.session ? 'session' : 'persistent';
- cb[7] = cookie.name;
- return cb.join('');
- };
-
- var cookieKeyFromCookieURL = function(url, type, name) {
- var ηmuri = ηm.URI.set(url);
- var cb = cookieKeyBuilder;
- cb[0] = ηmuri.scheme;
- cb[2] = ηmuri.hostname;
- cb[3] = ηmuri.path;
- cb[5] = type;
- cb[7] = name;
- return cb.join('');
- };
-
- /******************************************************************************/
-
- var cookieURLFromCookieEntry = function(entry) {
- if ( !entry ) {
- return '';
- }
- return (entry.secure ? 'https://' : 'http://') + entry.hostname + entry.path;
- };
-
- /******************************************************************************/
-
- var cookieMatchDomains = function(cookieKey, allHostnamesString) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
- if ( allHostnamesString.indexOf(' ' + cookieEntry.hostname + ' ') < 0 ) {
- if ( !cookieEntry.anySubdomain ) {
- return false;
- }
- if ( allHostnamesString.indexOf('.' + cookieEntry.hostname + ' ') < 0 ) {
- return false;
- }
- }
- return true;
- };
-
- /******************************************************************************/
+ let recordPageCookiesQueue = new Map();
+ let removePageCookiesQueue = new Map();
+ let removeCookieQueue = new Set();
+ let processRemoveQueuePeriod = 2 * 60 * 1000;
+ let processCleanPeriod = 10 * 60 * 1000;
+ let processPageRecordQueueTimer = null;
+ let processPageRemoveQueueTimer = null;
// Look for cookies to record for a specific web page
- var recordPageCookiesAsync = function(pageStats) {
+ let recordPageCookiesAsync = function (pageStats) {
// Store the page stats objects so that it doesn't go away
// before we handle the job.
// rhill 2013-10-19: pageStats could be nil, for example, this can
// happens if a file:// ... makes an xmlHttpRequest
- if ( !pageStats ) {
+ if (!pageStats) {
return;
}
recordPageCookiesQueue.set(pageStats.pageUrl, pageStats);
- if ( processPageRecordQueueTimer === null ) {
- processPageRecordQueueTimer = vAPI.setTimeout(processPageRecordQueue, 1000);
+ if (processPageRecordQueueTimer === null) {
+ processPageRecordQueueTimer =
+ vAPI.setTimeout(processPageRecordQueue, 1000);
}
};
- /******************************************************************************/
-
- var cookieLogEntryBuilder = [
+ let cookieLogEntryBuilder = [
'',
'{',
'',
@@ -211,168 +73,176 @@
'}'
];
- var recordPageCookie = function(pageStore, cookieKey) {
- if ( vAPI.isBehindTheSceneTabId(pageStore.tabId) ) { return; }
+ let recordPageCookie = function (pageStore, key) {
+ if (vAPI.isBehindTheSceneTabId(pageStore.tabId)) {
+ return;
+ }
- var cookieEntry = cookieDict.get(cookieKey);
- var pageHostname = pageStore.pageHostname;
- var block = ηm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie');
+ let entry = CookieCache.get(key);
+ let pageHostname = pageStore.pageHostname;
+ let block = ηm.mustBlock(pageHostname, entry.hostname, 'cookie');
- cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry);
- cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent';
- cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name);
+ cookieLogEntryBuilder[0] = CookieUtils.urlFromEntry(entry);
+ cookieLogEntryBuilder[2] = entry.session ? 'session' : 'persistent';
+ cookieLogEntryBuilder[4] = encodeURIComponent(entry.name);
- var cookieURL = cookieLogEntryBuilder.join('');
+ let cookieURL = cookieLogEntryBuilder.join('');
// rhill 2013-11-20:
// https://github.com/gorhill/httpswitchboard/issues/60
// Need to URL-encode cookie name
pageStore.recordRequest('cookie', cookieURL, block);
- ηm.logger.writeOne(pageStore.tabId, 'net', pageHostname, cookieURL, 'cookie', block);
+ ηm.logger.writeOne(pageStore.tabId, 'net',
+ pageHostname, cookieURL, 'cookie', block);
- cookieEntry.usedOn.add(pageHostname);
+ entry.usedOn.add(pageHostname);
// rhill 2013-11-21:
// https://github.com/gorhill/httpswitchboard/issues/65
// Leave alone cookies from behind-the-scene requests if
// behind-the-scene processing is disabled.
- if ( !block ) {
+ if (!block) {
return;
}
- if ( !ηm.userSettings.deleteCookies ) {
+ if (!ηm.userSettings.deleteCookies) {
return;
}
- removeCookieAsync(cookieKey);
+ removeCookieAsync(key);
};
- /******************************************************************************/
-
// Look for cookies to potentially remove for a specific web page
- var removePageCookiesAsync = function(pageStats) {
+ let removePageCookiesAsync = function (pageStats) {
// Hold onto pageStats objects so that it doesn't go away
// before we handle the job.
// rhill 2013-10-19: pageStats could be nil, for example, this can
// happens if a file:// ... makes an xmlHttpRequest
- if ( !pageStats ) {
+ if (!pageStats) {
return;
}
removePageCookiesQueue.set(pageStats.pageUrl, pageStats);
- if ( processPageRemoveQueueTimer === null ) {
- processPageRemoveQueueTimer = vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
+ if (processPageRemoveQueueTimer === null) {
+ processPageRemoveQueueTimer =
+ vAPI.setTimeout(processPageRemoveQueue, 15 * 1000);
}
};
- /******************************************************************************/
-
// Candidate for removal
- var removeCookieAsync = function(cookieKey) {
- removeCookieQueue.add(cookieKey);
+ let removeCookieAsync = function (key) {
+ removeCookieQueue.add(key);
};
- /******************************************************************************/
-
- var chromeCookieRemove = function(cookieEntry, name) {
- var url = cookieURLFromCookieEntry(cookieEntry);
- if ( url === '' ) {
+ let chromeCookieRemove = function (entry, name) {
+ let url = CookieUtils.urlFromEntry(entry);
+ if (url === '') {
return;
}
- var sessionCookieKey = cookieKeyFromCookieURL(url, 'session', name);
- var persistCookieKey = cookieKeyFromCookieURL(url, 'persistent', name);
- var callback = function(details) {
- var success = !!details;
- var template = success ? i18nCookieDeleteSuccess : i18nCookieDeleteFailure;
- if ( removeCookieFromDict(sessionCookieKey) ) {
- if ( success ) {
+ let sessionKey = CookieUtils.keyFromURL(UriTools.set(url),
+ 'session', name);
+ let persistKey = CookieUtils.keyFromURL(UriTools.set(url),
+ 'persistent', name);
+
+ let callback = function(details) {
+ let success = !!details;
+ let template = success ?
+ i18nCookieDeleteSuccess :
+ i18nCookieDeleteFailure;
+
+ if (CookieCache.remove(sessionKey)) {
+ if (success) {
ηm.cookieRemovedCounter += 1;
}
- ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', sessionCookieKey));
+ ηm.logger.writeOne('', 'info', 'cookie',
+ template.replace('{{value}}',
+ sessionKey));
}
- if ( removeCookieFromDict(persistCookieKey) ) {
- if ( success ) {
+ if (CookieCache.remove(persistKey)) {
+ if (success) {
ηm.cookieRemovedCounter += 1;
}
- ηm.logger.writeOne('', 'info', 'cookie', template.replace('{{value}}', persistCookieKey));
+ ηm.logger.writeOne('', 'info', 'cookie',
+ template.replace('{{value}}',
+ persistKey));
}
};
- vAPI.cookies.remove({ url: url, name: name }, callback);
+ vAPI.cookies.remove({
+ url: url, name: name
+ }, callback);
};
- var i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
- var i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
-
- /******************************************************************************/
+ let i18nCookieDeleteSuccess = vAPI.i18n('loggerEntryCookieDeleted');
+ let i18nCookieDeleteFailure = vAPI.i18n('loggerEntryDeleteCookieError');
- var processPageRecordQueue = function() {
+ let processPageRecordQueue = function () {
processPageRecordQueueTimer = null;
- for ( var pageStore of recordPageCookiesQueue.values() ) {
+ for (let pageStore of recordPageCookiesQueue.values()) {
findAndRecordPageCookies(pageStore);
}
recordPageCookiesQueue.clear();
};
- /******************************************************************************/
-
- var processPageRemoveQueue = function() {
+ let processPageRemoveQueue = function () {
processPageRemoveQueueTimer = null;
- for ( var pageStore of removePageCookiesQueue.values() ) {
+ for (let pageStore of removePageCookiesQueue.values()) {
findAndRemovePageCookies(pageStore);
}
removePageCookiesQueue.clear();
};
- /******************************************************************************/
-
// Effectively remove cookies.
- var processRemoveQueue = function() {
- var userSettings = ηm.userSettings;
- var deleteCookies = userSettings.deleteCookies;
+ let processRemoveQueue = function () {
+ let userSettings = ηm.userSettings;
+ let deleteCookies = userSettings.deleteCookies;
// Session cookies which timestamp is *after* tstampObsolete will
// be left untouched
// https://github.com/gorhill/httpswitchboard/issues/257
- var tstampObsolete = userSettings.deleteUnusedSessionCookies ?
- Date.now() - userSettings.deleteUnusedSessionCookiesAfter * 60 * 1000 :
+ let dusc = userSettings.deleteUnusedSessionCookies;
+ let dusca = userSettings.deleteUnusedSessionCookiesAfter;
+ let tstampObsolete = dusc ?
+ Date.now() - dusca * 60 * 1000 :
0;
- var srcHostnames;
- var cookieEntry;
+ let srcHostnames;
+ let entry;
- for ( var cookieKey of removeCookieQueue ) {
+ for (let key 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.get(cookieKey);
- if ( cookieEntry === undefined ) { continue; }
+ entry = CookieCache.get(key);
+ if (entry === undefined) {
+ continue;
+ }
// Delete obsolete session cookies: enabled.
- if ( tstampObsolete !== 0 && cookieEntry.session ) {
- if ( cookieEntry.tstamp < tstampObsolete ) {
- chromeCookieRemove(cookieEntry, cookieEntry.name);
+ if (tstampObsolete !== 0 && entry.session) {
+ if (entry.tstamp < tstampObsolete) {
+ chromeCookieRemove(entry, entry.name);
continue;
}
}
// Delete all blocked cookies: disabled.
- if ( deleteCookies === false ) {
+ if (deleteCookies === false) {
continue;
}
// Query scopes only if we are going to use them
- if ( srcHostnames === undefined ) {
+ if (srcHostnames === undefined) {
srcHostnames = ηm.tMatrix.extractAllSourceHostnames();
}
// Ensure cookie is not allowed on ALL current web pages: It can
// happen that a cookie is blacklisted on one web page while
// being whitelisted on another (because of per-page permissions).
- if ( canRemoveCookie(cookieKey, srcHostnames) ) {
- chromeCookieRemove(cookieEntry, cookieEntry.name);
+ if (canRemoveCookie(key, srcHostnames)) {
+ chromeCookieRemove(entry, entry.name);
}
}
@@ -381,21 +251,21 @@
vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
};
- /******************************************************************************/
-
// 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() {
- var us = ηm.userSettings;
- if ( us.deleteCookies || us.deleteUnusedSessionCookies ) {
- var cookieKeys = Array.from(cookieDict.keys()),
- len = cookieKeys.length,
- step, offset, n;
- if ( len > 25 ) {
+ let processClean = function () {
+ let us = ηm.userSettings;
+
+ if (us.deleteCookies || us.deleteUnusedSessionCookies) {
+ let keys = Array.from(CookieCache.keys());
+ let len = keys.length;
+ let step, offset, n;
+
+ if (len > 25) {
step = len / 25;
offset = Math.floor(Math.random() * len);
n = 25;
@@ -404,9 +274,10 @@
offset = 0;
n = len;
}
- var i = offset;
- while ( n-- ) {
- removeCookieAsync(cookieKeys[Math.floor(i % len)]);
+
+ let i = offset;
+ while (n--) {
+ removeCookieAsync(keys[Math.floor(i % len)]);
i += step;
}
}
@@ -414,57 +285,56 @@
vAPI.setTimeout(processClean, processCleanPeriod);
};
- /******************************************************************************/
-
- var findAndRecordPageCookies = function(pageStore) {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- recordPageCookie(pageStore, cookieKey);
+ let findAndRecordPageCookies = function (pageStore) {
+ for (let key of CookieCache.keys()) {
+ if (CookieUtils.matchDomains(key, pageStore.allHostnamesString)) {
+ recordPageCookie(pageStore, key);
}
}
};
- /******************************************************************************/
-
- var findAndRemovePageCookies = function(pageStore) {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
- removeCookieAsync(cookieKey);
+ let findAndRemovePageCookies = function (pageStore) {
+ for (let key of CookieCache.keys()) {
+ if (CookieUtils.matchDomains(key, pageStore.allHostnamesString)) {
+ removeCookieAsync(key);
}
}
};
- /******************************************************************************/
-
- var canRemoveCookie = function(cookieKey, srcHostnames) {
- var cookieEntry = cookieDict.get(cookieKey);
- if ( cookieEntry === undefined ) { return false; }
+ let canRemoveCookie = function (key, srcHostnames) {
+ let entry = CookieCache.get(key);
+ if (entry === undefined) {
+ return false;
+ }
- var cookieHostname = cookieEntry.hostname;
- var srcHostname;
+ let cookieHostname = entry.hostname;
+ let srcHostname;
- for ( srcHostname of cookieEntry.usedOn ) {
- if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ for (srcHostname of entry.usedOn) {
+ if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) {
return false;
}
}
+
// Maybe there is a scope in which the cookie is 1st-party-allowed.
// For example, if I am logged in into `github.com`, I do not want to be
// logged out just because I did not yet open a `github.com` page after
// re-starting the browser.
srcHostname = cookieHostname;
- var pos;
+
+ let pos;
+
for (;;) {
- if ( srcHostnames.has(srcHostname) ) {
- if ( ηm.mustAllow(srcHostname, cookieHostname, 'cookie') ) {
+ if (srcHostnames.has(srcHostname)) {
+ if (ηm.mustAllow(srcHostname, cookieHostname, 'cookie')) {
return false;
}
}
- if ( srcHostname === cookieEntry.domain ) {
+ if (srcHostname === entry.domain) {
break;
}
pos = srcHostname.indexOf('.');
- if ( pos === -1 ) {
+ if (pos === -1) {
break;
}
srcHostname = srcHostname.slice(pos + 1);
@@ -472,81 +342,73 @@
return true;
};
- /******************************************************************************/
-
// Listen to any change in cookieland, we will update page stats accordingly.
- vAPI.cookies.onChanged = function(cookie) {
+ 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.get(cookieKey);
- if ( cookieEntry === undefined ) {
- cookieEntry = addCookieToDict(cookie);
+ let key = CookieUtils.keyFromCookie(cookie);
+ let entry = CookieCache.get(key);
+
+ if (entry === undefined) {
+ entry = CookieCache.add(cookie);
} else {
- cookieEntry.tstamp = Date.now();
- if ( cookie.value === cookieEntry.value ) { return; }
- cookieEntry.value = cookie.value;
+ entry.tstamp = Date.now();
+ if (cookie.value === entry.value) {
+ return;
+ }
+ entry.value = cookie.value;
}
// Go through all pages and update if needed, as one cookie can be used
// by many web pages, so they need to be recorded for all these pages.
- var pageStores = ηm.pageStores;
- var pageStore;
- for ( var tabId in pageStores ) {
- if ( pageStores.hasOwnProperty(tabId) === false ) {
+ let pageStores = ηm.pageStores;
+ let pageStore;
+ for (let tabId in pageStores) {
+ if (pageStores.hasOwnProperty(tabId) === false) {
continue;
}
pageStore = pageStores[tabId];
- if ( !cookieMatchDomains(cookieKey, pageStore.allHostnamesString) ) {
+ if (!CookieUtils.matchDomains(key, pageStore.allHostnamesString)) {
continue;
}
- recordPageCookie(pageStore, cookieKey);
+ recordPageCookie(pageStore, key);
}
};
- /******************************************************************************/
-
// Listen to any change in cookieland, we will update page stats accordingly.
- vAPI.cookies.onRemoved = function(cookie) {
- var cookieKey = cookieKeyFromCookie(cookie);
- if ( removeCookieFromDict(cookieKey) ) {
- ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ vAPI.cookies.onRemoved = function (cookie) {
+ let key = CookieUtils.keyFromCookie(cookie);
+ if (CookieCache.remove(key)) {
+ ηm.logger.writeOne('', 'info', 'cookie',
+ i18nCookieDeleteSuccess.replace('{{value}}',
+ key));
}
};
- /******************************************************************************/
-
// Listen to any change in cookieland, we will update page stats accordingly.
- vAPI.cookies.onAllRemoved = function() {
- for ( var cookieKey of cookieDict.keys() ) {
- if ( removeCookieFromDict(cookieKey) ) {
- ηm.logger.writeOne('', 'info', 'cookie', i18nCookieDeleteSuccess.replace('{{value}}', cookieKey));
+ vAPI.cookies.onAllRemoved = function () {
+ for (let key of CookieCache.keys()) {
+ if (CookieCache.remove(key)) {
+ ηm.logger.writeOne('', 'info', 'cookie',
+ i18nCookieDeleteSuccess.replace('{{value}}',
+ key));
}
}
};
- /******************************************************************************/
-
- vAPI.cookies.getAll(addCookiesToDict);
+ vAPI.cookies.getAll(CookieCache.addVector);
vAPI.cookies.start();
vAPI.setTimeout(processRemoveQueue, processRemoveQueuePeriod);
vAPI.setTimeout(processClean, processCleanPeriod);
- /******************************************************************************/
-
// Expose only what is necessary
return {
recordPageCookies: recordPageCookiesAsync,
removePageCookies: removePageCookiesAsync
};
-
- /******************************************************************************/
-
})();
-
-/******************************************************************************/