diff options
author | Alessio Vanni <vannilla@firemail.cc> | 2020-08-24 14:34:54 +0200 |
---|---|---|
committer | Jesús <heckyel@hyperbola.info> | 2022-04-06 04:39:51 +0800 |
commit | b769343d0511aa444a6bf9fd601f0f97dea27cab (patch) | |
tree | 70cbea11cb43455e246dc3b75cb3759903f786c0 | |
parent | f7ee828e1c624be7429f8d986612c600d3eefd3b (diff) | |
download | ematrix-b769343d0511aa444a6bf9fd601f0f97dea27cab.tar.lz ematrix-b769343d0511aa444a6bf9fd601f0f97dea27cab.tar.xz ematrix-b769343d0511aa444a6bf9fd601f0f97dea27cab.zip |
Rewrite messaging.js
Before, it was a series of switch statements that did stuff based on
the content of a field of the request object; the new implementation
does dynamic dispatching of "methods" (just some functions in a table)
instead.
Signed-off-by: Jesús <heckyel@hyperbola.info>
-rw-r--r-- | js/messaging.js | 1540 |
1 files changed, 738 insertions, 802 deletions
diff --git a/js/messaging.js b/js/messaging.js index 90c5c5f..63e1c92 100644 --- a/js/messaging.js +++ b/js/messaging.js @@ -17,849 +17,785 @@ 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://libregit.spks.xyz/heckyel/ematrix + Home: https://notabug.org/heckyel/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ 'use strict'; -// Default handler (function () { + Cu.import('chrome://ematrix/content/lib/UriTools.jsm'); Cu.import('chrome://ematrix/content/lib/Tools.jsm'); + Cu.import('chrome://ematrix/content/lib/RowSnapshot.jsm'); let ηm = ηMatrix; - // Default is for commonly used message. - function onMessage(request, sender, callback) { - // Async - switch (request.what) { - case 'getAssetContent': - ηm.assets.get(request.url, { - dontCache: true, - }, callback); - return; - case 'selectHostsFiles': - ηm.selectHostsFiles(request, callback); - return; - default: - break; - } - - // Sync - let response; - - switch (request.what) { - case 'forceReloadTab': - ηm.forceReload(request.tabId, request.bypassCache); - break; - case 'forceUpdateAssets': - ηm.scheduleAssetUpdater(0); - ηm.assets.updateStart({ - delay: 2000, - }); - break; - case 'getUserSettings': - response = { - userSettings: ηm.userSettings, - matrixSwitches: { - 'https-strict': ηm.pMatrix.evaluateSwitch('https-strict', '*') === 1, - 'referrer-spoof': ηm.pMatrix.evaluateSwitch('referrer-spoof', '*') === 1, - 'noscript-spoof': ηm.pMatrix.evaluateSwitch('noscript-spoof', '*') === 1, - }, - }; - break; - case 'gotoExtensionURL': - Tools.gotoExtensionURL({ - api: vAPI, - details: request, - matrix: ηm, - }); - break; - case 'gotoURL': - Tools.gotoURL({ - api: vAPI, - details: request, - }); - break; - case 'mustBlock': - response = ηm.mustBlock(request.scope, - request.hostname,request.type); - break; - case 'readRawSettings': - response = ηm.stringFromRawSettings(); - break; - case 'reloadHostsFiles': - ηm.reloadHostsFiles(); - break; - case 'setMatrixSwitch': - ηm.tMatrix.setSwitch(request.switchName, '*', request.state); - if (ηm.pMatrix.setSwitch(request.switchName, '*', request.state)) { - ηm.saveMatrix(); - } - break; - case 'userSettings': - if (request.hasOwnProperty('value') === false) { - request.value = undefined; - } - response = ηm.changeUserSettings(request.name, request.value); - break; - case 'writeRawSettings': - ηm.rawSettingsFromString(request.content); - break; - default: - return vAPI.messaging.UNHANDLED; - } - callback(response); - } - - vAPI.messaging.setup(onMessage); -})(); - -(function () { - // popup.js - - let ηm = ηMatrix; - - // Constructor is faster than object literal - // ηMatrix: really? I'll take your word for it... - let RowSnapshot = function(srcHostname, desHostname, desDomain) { - this.domain = desDomain; - this.temporary = ηm.tMatrix.evaluateRowZXY(srcHostname, desHostname); - this.permanent = ηm.pMatrix.evaluateRowZXY(srcHostname, desHostname); - this.counts = RowSnapshot.counts.slice(); - this.totals = RowSnapshot.counts.slice(); - }; - - RowSnapshot.counts = (function () { - let aa = []; - let n = ηm.Matrix.columnHeaderIndices.size - - for (let i=0; i<n; ++i) { - aa[i] = 0; - } - - return aa; - })(); - - let matrixSnapshot = function (pageStore, details) { - let ηmuser = ηm.userSettings; - let headerIndices = ηm.Matrix.columnHeaderIndices; - - let r = { - appVersion: vAPI.app.version, - blockedCount: pageStore.requestStats.blocked.all, - collapseAllDomains: ηmuser.popupCollapseAllDomains, - collapseBlacklistedDomains: ηmuser.popupCollapseBlacklistedDomains, - diff: [], - domain: pageStore.pageDomain, - has3pReferrer: pageStore.has3pReferrer, - hasMixedContent: pageStore.hasMixedContent, - hasNoscriptTags: pageStore.hasNoscriptTags, - hasWebWorkers: pageStore.hasWebWorkers, - headerIndices: Array.from(headerIndices), - hostname: pageStore.pageHostname, - mtxContentModified: pageStore.mtxContentModifiedTime !== details.mtxContentModifiedTime, - mtxCountModified: pageStore.mtxCountModifiedTime !== details.mtxCountModifiedTime, - mtxContentModifiedTime: pageStore.mtxContentModifiedTime, - mtxCountModifiedTime: pageStore.mtxCountModifiedTime, - pMatrixModified: ηm.pMatrix.modifiedTime !== details.pMatrixModifiedTime, - pMatrixModifiedTime: ηm.pMatrix.modifiedTime, - pSwitches: {}, - rows: {}, - rowCount: 0, - scope: '*', - tabId: pageStore.tabId, - tMatrixModified: ηm.tMatrix.modifiedTime !== details.tMatrixModifiedTime, - tMatrixModifiedTime: ηm.tMatrix.modifiedTime, - tSwitches: {}, - url: pageStore.pageUrl, - userSettings: { - colorBlindFriendly: ηmuser.colorBlindFriendly, - displayTextSize: ηmuser.displayTextSize, - popupScopeLevel: ηmuser.popupScopeLevel - }, - }; - - if (typeof details.scope === 'string') { - r.scope = details.scope; - } else if (ηmuser.popupScopeLevel === 'site') { - r.scope = r.hostname; - } else if (ηmuser.popupScopeLevel === 'domain') { - r.scope = r.domain; - } - - for (let switchName of ηm.Matrix.switchNames) { - r.tSwitches[switchName] = - ηm.tMatrix.evaluateSwitchZ(switchName, r.scope); - - r.pSwitches[switchName] = - ηm.pMatrix.evaluateSwitchZ(switchName, r.scope); - } - - // These rows always exist - r.rows['*'] = new RowSnapshot(r.scope, '*', '*'); - r.rows['1st-party'] = new RowSnapshot(r.scope, - '1st-party', '1st-party'); - r.rowCount += 1; - - let anyIndex = headerIndices.get('*'); - for (let entry of pageStore.hostnameTypeCells) { - let pos = entry[0].indexOf(' '); - let reqHostname = entry[0].slice(0, pos); - let reqType = entry[0].slice(pos + 1); - - // rhill 2013-10-23: hostname can be empty if the request - // is a data url - // https://github.com/gorhill/httpswitchboard/issues/26 - if (reqHostname === '') { - reqHostname = pageStore.pageHostname; - } - - let reqDomain = - UriTools.domainFromHostname(reqHostname) || reqHostname; - - // We want rows of self and ancestors - let desHostname = reqHostname; - for (;;) { - // If row exists, ancestors exist - if (r.rows.hasOwnProperty(desHostname) !== false) { - break; - } - - r.rows[desHostname] = - new RowSnapshot(r.scope, desHostname, reqDomain); - r.rowCount += 1; - - if (desHostname === reqDomain) { - break; - } - - pos = desHostname.indexOf('.'); - if (pos === -1) { - break; - } - - desHostname = desHostname.slice(pos + 1); - } - - let count = entry[1].size; - let typeIndex = headerIndices.get(reqType); - - let row = r.rows[reqHostname]; - row.counts[typeIndex] += count; - row.counts[anyIndex] += count; - row = r.rows[reqDomain]; - row.totals[typeIndex] += count; - row.totals[anyIndex] += count; - row = r.rows['*']; - row.totals[typeIndex] += count; - row.totals[anyIndex] += count; - } - - r.diff = ηm.tMatrix.diff(ηm.pMatrix, r.hostname, Object.keys(r.rows)); - - return r; - }; - - let matrixSnapshotFromTabId = function (details, callback) { - let matrixSnapshotIf = function (tabId, details) { - let pageStore = ηm.pageStoreFromTabId(tabId); - if (pageStore === null) { - callback('ENOTFOUND'); - return; - } - - // First verify whether we must return data or not. - if (ηm.tMatrix.modifiedTime === details.tMatrixModifiedTime - && ηm.pMatrix.modifiedTime === details.pMatrixModifiedTime - && pageStore.mtxContentModifiedTime === details.mtxContentModifiedTime - && pageStore.mtxCountModifiedTime === details.mtxCountModifiedTime) { - callback('ENOCHANGE'); - return ; - } - - callback(matrixSnapshot(pageStore, details)); - }; - - // Specific tab id requested? - if (details.tabId) { - matrixSnapshotIf(details.tabId, details); - return; - } - - // Fall back to currently active tab - let onTabReady = function (tab) { - if (tab instanceof Object === false) { - callback('ENOTFOUND'); - return; - } - - // Allow examination of behind-the-scene requests - let tabId = - tab.url.lastIndexOf(vAPI.getURL('dashboard.html'), 0) !== 0 ? - tab.id : - vAPI.noTabId; - matrixSnapshotIf(tabId, details); - }; - - vAPI.tabs.get(null, onTabReady); - }; - - let onMessage = function (request, sender, callback) { - // Async - switch (request.what) { - case 'matrixSnapshot': - matrixSnapshotFromTabId(request, callback); - return; - default: - break; - } - - // Sync - let response; - switch (request.what) { - case 'toggleMatrixSwitch': - let switchZ = ηm.tMatrix.evaluateSwitchZ(request.switchName, - request.srcHostname) - ηm.tMatrix.setSwitchZ(request.switchName, - request.srcHostname, - switchZ === false); - break; - case 'blacklistMatrixCell': - ηm.tMatrix.blacklistCell(request.srcHostname, - request.desHostname, - request.type); - break; - case 'whitelistMatrixCell': - ηm.tMatrix.whitelistCell(request.srcHostname, - request.desHostname, - request.type); - break; - case 'graylistMatrixCell': - ηm.tMatrix.graylistCell(request.srcHostname, - request.desHostname, - request.type); - break; - case 'applyDiffToPermanentMatrix': // aka "persist" - if (ηm.pMatrix.applyDiff(request.diff, ηm.tMatrix)) { - ηm.saveMatrix(); - } - break; - case 'applyDiffToTemporaryMatrix': // aka "revert" - ηm.tMatrix.applyDiff(request.diff, ηm.pMatrix); - break; - case 'revertTemporaryMatrix': - ηm.tMatrix.assign(ηm.pMatrix); - break; - default: - return vAPI.messaging.UNHANDLED; - } - - callback(response); - }; - - vAPI.messaging.listen('popup.js', onMessage); -})(); - -// content scripts -(function () { - - let ηm = ηMatrix; - - let foundInlineCode = function (tabId, pageStore, details, type) { - if (pageStore === null) { - return; - } - - let pageHostname = pageStore.pageHostname; - let ηmuri = UriTools.set(details.documentURI); - let frameURL = UriTools.normalizedURI(); - - let blocked = details.blocked; - if (blocked === undefined) { - blocked = ηm.mustBlock(pageHostname, ηmuri.hostname, type); - } - - let mapTo = { - css: 'style', - script: 'script', - }; - - // https://github.com/gorhill/httpswitchboard/issues/333 - // Look-up here whether inline scripting is blocked for the frame. - let url = frameURL + '{inline_' + mapTo[type] + '}'; - pageStore.recordRequest(type, url, blocked); - ηm.logger.writeOne(tabId, 'net', pageHostname, url, type, blocked); + // I personally find this method better than a giant (or multiple + // giant) switch statement(s), but all things considered it's not + // as nice as when the language has proper macros. The idea is to + // make a more flexible system, at the cost of a slightly more + // verbose code and a bit more memory usage due to the new + // function objects. + + // Method tables. + // "Early" is for those methods that do not call the callback. + let methods = {}; + let methodsEarly = {}; + + // The message queue callback. + let dispatchMethod = function (request, sender, cb) { + let f = methods[request.what]; + let early = false; + if (!f) { + f = methodsEarly[request.what]; + if (!f) { + return vAPI.messaging.UNHANDLED; + } + early = true; + } + + let response = f(request, sender, cb); + if (early === true) { + return response; + } + + cb(response); }; - let contentScriptLocalStorageHandler = function (tabId, originURL) { - let tabContext = ηm.tabContextManager.lookup(tabId); - if (tabContext === null) { - return; - } + // Used here to add dispatchable functions + let addMethod = function (name, f, /* &optional */ early) { + if (early) { + methodsEarly[name] = f; + } else { + methods[name] = f; + } + } - let blocked = ηm.mustBlock(tabContext.rootHostname, - UriTools.hostnameFromURI(originURL), - 'cookie'); + // Default + addMethod('getAssetContent', function (request, sender, cb) { + ηm.assets.get(request.url, { + dontCache: true, + }, cb); + return undefined; + }, true); + + addMethod('selectHostsFiles', function (request, sender, cb) { + ηm.selectHostsFiles(request, cb); + return undefined; + }, true); + + addMethod('forceReloadTab', function (request, sender, cb) { + ηm.forceReload(request.tabId, request.bypassCache); + return undefined; + }); + + addMethod('forceUpdateAssets', function (request, sender, cb) { + ηm.scheduleAssetUpdater(0); + ηm.assets.updateStart({ + delay: 1000, + }); + return undefined; + }); + + addMethod('getUserSettings', function (request, sender, cb) { + let pmat = ηm.pMatrix; + return { + userSettings: ηm.userSettings, + matrixSwitches: { + 'https-strict': (pmat.evaluateSwitch('https-strict', '*') + === 1), + 'referrer-spoof': (pmat.evaluateSwitch('referrer-spoof', '*') + === 1), + 'noscript-spoof': (pmat.evaluateSwitch('noscript-spoof', '*') + === 1), + }, + }; + }); + + addMethod('gotoExtensionURL', function (request, sender, cb) { + Tools.gotoExtensionURL({ + api: vAPI, + details: request, + matrix: ηm, + }); + return undefined; + }); + + addMethod('gotoURL', function (request, sender, cb) { + Tools.gotoURL({ + api: vAPI, + details: request, + }); + return undefined; + }); + + addMethod('mustBlock', function (request, sender, cb) { + return ηm.mustBlock(request.scope, + request.hostname, + request.type); + }); + + addMethod('readRawSettings', function (request, sender, cb) { + return ηm.stringFromRawSettings(); + }); + + addMethod('writeRawSettings', function (request, sender, cb) { + ηm.rawSettingsFromString(request.content); + return undefined; + }); + + addMethod('reloadHostsFiles', function (request, sender, cb) { + ηm.reloadHostsFiles(); + return undefined; + }); + + addMethod('setMatrixSwitch', function (request, sender, cb) { + ηm.tMatrix.setSwitch(request.switchName, '*', request.state); + if (ηm.pMatrix.setSwitch(request.swtichName, '*', request.state)) { + ηm.saveMatrix(); + } + return undefined; + }); + + addMethod('userSettings', function (request, sender, cb) { + if (request.hasOwnProperty('value') === false) { + request.value = undefined; + } + return ηm.changeUserSettings(request.name, request.value); + }); + + vAPI.messaging.setup(dispatchMethod); + + // popup.js begins here + addMethod('matrixSnapshot', function (request, sender, cb) { + let snapShot = function (store, details) { + let user = ηm.userSettings; + let indices = ηm.Matrix.columnHeaderIndices; + + let r = { + appVersion: vAPI.app.version, + blockedCount: store.requestStats.blocked.all, + collapseAllDomains: user.popupCollapseAllDomains, + collapseBlacklistedDomains: user.popupCollapseBlacklistedDomains, + diff: [], + domain: store.pageDomain, + has3pReferrer: store.has3pReferrer, + hasMixedContent: store.hasMixedContent, + hasNoscriptTags: store.hasNoscriptTags, + hasWebWorkers: store.hasWebWorkers, + headerIndices: Array.from(indices), + hostname: store.pageHostname, + mtxContentModified: (store.mtxContentModifiedTime + !== details.mtxContentModifiedTime), + mtxCountModified: (store.mtxCountModifiedTime + !== details.mtxCountModifiedTime), + mtxContentModifiedTime: store.mtxContentModifiedTime, + mtxCountModifiedTime: store.mtxCountModifiedTime, + pMatrixModified: (ηm.pMatrix.modifiedTime + !== details.pMatrixModifiedTime), + pMatrixModifiedTime: ηm.pMatrix.modifiedTime, + pSwitches: {}, + rows: {}, + rowCount: 0, + scope: '*', + tabId: store.tabId, + tMatrixModified: (ηm.tMatrix.modifiedTime + !== details.tMatrixModifiedTime), + tMatrixModifiedTime: ηm.tMatrix.modifiedTime, + tSwitches: {}, + url: store.pageUrl, + userSettings: { + colorBlindFriendly: user.colorBlindFriendly, + displayTextSize: user.displayTextSize, + popupScopeLevel: user.popupScopeLevel, + }, + }; + + if (typeof details.scope === 'string') { + r.scope = details.scope; + } else if (user.popupScopeLevel === 'site') { + r.scope = r.hostname; + } else if (user.popupScopeLevel === 'domain') { + r.scope = r.domain; + } + + for (let s of ηm.Matrix.switchNames) { + r.tSwitches[s] = ηm.tMatrix.evaluateSwitchZ(s, r.scope); + r.pSwitches[s] = ηm.pMatrix.evaluateSwitchZ(s, r.scope); + } + + r.rows['*'] = new RowSnapshot(ηm, r.scope, '*', '*'); + r.rows['1st-party'] = new RowSnapshot(ηm, r.scope, + '1st-party', '1st-party'); + r.rowCount += 1; + + let any = indices.get('*'); + for (let e of store.hostnameTypeCells) { + let pos = e[0].indexOf(' '); + let reqhn = e[0].slice(0, pos); + let reqt = e[0].slice(pos+1); + + // Hostname can be empty in some cases + // (reported from uMatrix) + if (reqhn === '') { + reqhn = store.pageHostname; + } + + let reqd = UriTools.domainFromHostname(reqhn) || reqhn; + + let desthn = reqhn; + while (true) { + if (r.rows.hasOwnProperty(desthn) !== false) { + break; + } + + r.rows[desthn] = new RowSnapshot(ηm, + r.scope, + desthn, + reqd); + r.rowCount += 1; + + if (desthn === reqd) { + break; + } + + pos = desthn.indexOf('.'); + if (pos === -1) { + break; + } + + desthn = desthn.slice(pos + 1); + } + + let count = e[1].size; + let tindex = indices.get(reqt); + + let row = r.rows[reqhn]; + row.counts[tindex] += count; + row.counts[any] += count; + + row = r.rows[reqd]; + row.totals[tindex] += count; + row.totals[any] += count; + + row = r.rows['*']; + row.totals[tindex] += count; + row.totals[any] += count; + } + + r.diff = ηm.tMatrix.diff(ηm.pMatrix, + r.hostname, + Object.keys(r.rows)); + + return r; + }; + + let snapFromId = function (details, cb) { + let snapIf = function (id, details) { + let store = ηm.pageStoreFromTabId(id); + if (store === null) { + cb('ENOTFOUND'); + return; + } + + let tmat = ηm.tMatrix; + let pmat = ηm.pMatrix; + let mtx = details.mtxContentModifiedTime; + + if (tmat.modifiedTime === details.tMatrixModifiedTime + && pmat.modifiedTime === details.pMatrixModifiedTime + && store.mtxContentModifiedTime === mtx + && store.mtxCountModifiedTime === mtx) { + cb('ENOCHANGE'); + return; + } + + cb(snapShot(store, details)); + }; + + if (details.tabId) { + snapIf(details.tabId, details); + return; + } + + let onReady = function (tab) { + if (tab instanceof Object === false) { + cb('ENOTFOUND'); + return; + } + + let id = + tab.url.lastIndexOf(vAPI.getURL('dashboard.html'), 0) !== 0 + ? tab.id + : vAPI.noTabId; + + snapIf(id, details); + }; + + vAPI.tabs.get(null, onReady); + }; + + snapFromId(request, cb); + return undefined; + }, true); + + addMethod('toggleMatrixSwitch', function (request, sender, cb) { + let sz = ηm.tMatrix.evaluateSwitchZ(request.switchName, + request.srcHostname); + ηm.tMatrix.setSwitchZ(request.switchName, + request.scrHostname, + sz === false); + return undefined; + }); + + addMethod('blacklistMatrixCell', function (request, sender, cb) { + ηm.tMatrix.blacklistCell(request.srcHostname, + request.desHostname, + request.type); + return undefined; + }); + + addMethod('whitelistMatrixCell', function (request, sender, cb) { + ηm.tMatrix.whitelistCell(request.srcHostname, + request.desHostname, + request.type); + return undefined; + }); + + addMethod('graylistMatrixCell', function (request, sender, cb) { + ηm.tMatrix.graylistCell(request.srcHostname, + request.desHostname, + request.type); + return undefined; + }); + + addMethod('applyDiffToPermanentMatrix', function (request, sender, cb) { + if (ηm.pMatrix.applyDiff(request.diff, ηm.tMatrix)) { + ηm.saveMatrix(); + } + return undefined; + }); + + addMethod('applyDiffToTemporaryMatrix', function (request, sender, cb) { + ηm.tMatrix.applyDiff(request.diff, ηm.pMatrix); + return undefined; + }); + + addMethod('revertTemporaryMatrix', function (request, sender, cb) { + ηm.tMatrix.assign(ηm.pMatrix); + return undefined; + }); + + vAPI.messaging.listen('popup.js', dispatchMethod); + + // Content scripts handling begins here + let placeholders = undefined; + let phReadTime = 0; + + addMethod('contentScriptHasLocalStorage', function (request, sender, cb) { + let contentScriptLocalStorageHandler = function (id, originURL) { + let tabContext = ηm.tabContextManager.lookup(id); + if (tabContext === null) { + return; + } + + let blocked = ηm.mustBlock(tabContext.rootHostname, + UriTools.hostnameFromURI(originURL), + 'cookie'); + + let store = ηm.pageStoreFromTabId(id); + if (store !== null) { + let requestURL = originURL + '/{localStorage}'; + store.recordRequest('cookie', requestURL, blocked); + ηm.logger.writeOne(id, 'net', tabContext.rootHostname, + requestURL, 'cookie', blocked); + } + + let removeStorage = blocked && ηm.userSettings.deleteLocalStorage; + if (removeStorage) { + ηm.localStorageRemovedCounter++; + } + + return removeStorage; + }; + + let tabId = sender && sender.tab ? sender.tab.id || 0 : 0; + return contentScriptLocalStorageHandler(tabId, request.originURL); + }); + + addMethod('lookupBlockedCollapsibles', function (request, sender, cb) { + let lookupBlockedCollapsibles = function (id, requests) { + if (phReadTime < ηm.rawSettingsWriteTime) { + placeholders = undefined; + } + + if (placeholders === undefined) { + placeholders = { + frame: ηm.rawSettings.framePlaceholder, + image: ηm.rawSettings.imagePlaceholder, + }; + + if (placeholders.frame) { + let doc = ηm.rawSettings.framePlaceholderDocument; + let bg = ηm.rawSettings.framePlaceholderBackground; + + placeholders.frameDocument = + doc.replace('{{bg}}', + bg !== 'default' + ? bg + : ηm.rawSettings.placeHolderBackground); + } + + if (placeholders.image) { + placeholders.imageBorder = + ηm.rawSettings.imagePlaceholderBorder !== 'default' + ? ηm.rawSettings.imagePlaceholderBorder + : ηm.rawSettings.placeholderBorder; + + placeholders.imageBackground = + ηm.rawSettings.imagePlaceholderBackground !== 'default' + ? ηm.rawSettings.imagePlaceholderBackground + : ηm.rawSettings.placeholderBackground; + } + + phReadTime = Date.now(); + } + + let response = { + blockedResources: [], + hash: requests.hash, + id: requests.id, + placeholders: placeholders, + }; + + let context = ηm.tabContextManager.lookup(id); + if (context === null) { + return response; + } + + let store = ηm.pageStoreFromTabId(id); + if (store !== null) { + store.lookupBlockedCollapsibles(requests, response); + } + + return response; + }; + + let tabId = sender && sender.tab ? sender.tab.id || 0 : 0; + return lookupBlockedCollapsibles(tabId, request); + }); + + addMethod('mustRenderNoscriptTags?', function (request, sender, cb) { + let tabId = sender && sender.tab ? sender.tab.id || 0 : 0; + let tabContext = ηm.tabContextManager.lookup(tabId); + let rootHostname = tabContext && tabContext.rootHostname; + let pageStore = ηm.pageStoreFromTabId(tabId); + + if (tabContext === null) { + return undefined; + } + + let response = + ηm.tMatrix.mustBlock(rootHostname, rootHostname, 'script') + && ηm.tMatrix.evaluateSwitchZ('noscript-spoof', rootHostname); - let pageStore = ηm.pageStoreFromTabId(tabId); if (pageStore !== null) { - let requestURL = originURL + '/{localStorage}'; - pageStore.recordRequest('cookie', requestURL, blocked); - ηm.logger.writeOne(tabId, 'net', tabContext.rootHostname, - requestURL, 'cookie', blocked); + pageStore.hasNoscriptTags = true; } - let removeStorage = blocked && ηm.userSettings.deleteLocalStorage; - if (removeStorage) { - ηm.localStorageRemovedCounter++; + // https://github.com/gorhill/uMatrix/issues/225 + // A good place to force an update of the page title, as + // at this point the DOM has been loaded. + ηm.updateTitle(tabId); + + return response; + }); + + addMethod('securityPolicyViolation', function (request, sender, cb) { + let foundInlineCode = function (id, store, details, type) { + if (store === null) { + return; + } + + let pageHostname = store.pageHostname; + let uri = UriTools.set(details.documentURI); + let frameURL = UriTools.normalizedURI(); + + let blocked = details.blocked; + if (blocked === undefined) { + blocked = ηm.mustBlock(pageHostname, uri.hostname, type); + } + + let mapTo = { + css: 'style', + script: 'script', + }; + + // https://github.com/gorhill/httpswitchboard/issues/333 + // Look-up here whether inline scripting is blocked for the frame. + let url = frameURL + '{inline_' + mapTo[type] + '}'; + store.recordRequest(type, url, blocked); + ηm.logger.writeOne(id, 'net', pageHostname, url, type, blocked); + }; + + let tabId = sender && sender.tab ? sender.tab.id || 0 : 0; + let tabContext = ηm.tabContextManager.lookup(tabId); + let rootHostname = tabContext && tabContext.rootHostname; + let pageStore = ηm.pageStoreFromTabId(tabId); + + if (request.directive === 'worker-src') { + let url = UriTools.hostnameFromURI(request.blockedURI) !== '' + ? request.blockedURI + : request.documentURI; + + if (pageStore !== null) { + pageStore.hasWebWorkers = true; + pageStore.recordRequest('script', url, request.blocked); + } + + if (tabContext !== null) { + ηm.logger.writeOne(tabId, 'net', rootHostname, + url, 'worker', request.blocked); + } + } else if (request.directive === 'script-src') { + foundInlineCode(tabId, pageStore, request, 'script'); + } else if (request.directive === 'style-src') { + foundInlineCode(tabId, pageStore, request, 'css'); } - return removeStorage; - }; + return undefined; + }); - // Evaluate many URLs against the matrix. - let lookupBlockedCollapsibles = function (tabId, requests) { - if (placeholdersReadTime < ηm.rawSettingsWriteTime) { - placeholders = undefined; - } + addMethod('shutdown?', function (request, sender, cb) { + let tabId = sender && sender.tab ? sender.tab.id || 0 : 0; + let tabContext = ηm.tabContextManager.lookup(tabId); + let rootHostname = tabContext && tabContext.rootHostname; - if (placeholders === undefined) { - placeholders = { - frame: ηm.rawSettings.framePlaceholder, - image: ηm.rawSettings.imagePlaceholder, - }; - if (placeholders.frame) { - placeholders.frameDocument = - ηm.rawSettings - .framePlaceholderDocument - .replace('{{bg}}', - ηm.rawSettings.framePlaceholderBackground !== 'default' ? - ηm.rawSettings.framePlaceholderBackground : - ηm.rawSettings.placeholderBackground); - } - if (placeholders.image) { - placeholders.imageBorder = - ηm.rawSettings.imagePlaceholderBorder !== 'default' ? - ηm.rawSettings.imagePlaceholderBorder : - ηm.rawSettings.placeholderBorder; - placeholders.imageBackground = - ηm.rawSettings.imagePlaceholderBackground !== 'default' ? - ηm.rawSettings.imagePlaceholderBackground : - ηm.rawSettings.placeholderBackground; - } - placeholdersReadTime = Date.now(); + let response = undefined; + if (tabContext !== null) { + response = + ηm.tMatrix.evaluateSwitchZ('matrixb-off', rootHostname); } - - let response = { - blockedResources: [], - hash: requests.hash, - id: requests.id, - placeholders: placeholders, + return response; + }); + + vAPI.messaging.listen('contentscript.js', dispatchMethod); + + // cloud-ui.js begins here + addMethod('cloudGetOptions', function (request, sender, cb) { + vAPI.cloud.getOptions(function (options) { + options.enabled = ηm.userSettings.cloudStorageEnabled === true; + cb(options); + }); + return undefined; + }, true); + + addMethod('cloudSetOptions', function (request, sender, cb) { + vAPI.cloud.setOptions(request.options, cb); + return undefined; + }, true); + + addMethod('cloudPull', function (request, sender, cb) { + return vAPI.cloud.pull(request.datakey, cb); + }, true); + + addMethod('cloudPush', function (request, sender, cb) { + return vAPI.cloud.push(request.datakey, request.data, cb); + }, true); + + vAPI.messaging.listen('cloud-ui.js', dispatchMethod); + + // user-rules.js begins here + addMethod('getUserRules', function (request, sender, cb) { + return { + temporaryRules: ηm.tMatrix.toString(), + permanentRules: ηm.pMatrix.toString(), }; + }); - let tabContext = ηm.tabContextManager.lookup(tabId); - if (tabContext === null) { - return response; - } - - let pageStore = ηm.pageStoreFromTabId(tabId); - if (pageStore !== null) { - pageStore.lookupBlockedCollapsibles(requests, response); - } - - return response; - }; - - let placeholders; - let placeholdersReadTime = 0; - - let onMessage = function (request, sender, callback) { - // Async - switch (request.what) { - default: - break; - } - - let tabId = sender && sender.tab ? sender.tab.id || 0 : 0; - let tabContext = ηm.tabContextManager.lookup(tabId); - let rootHostname = tabContext && tabContext.rootHostname; - let pageStore = ηm.pageStoreFromTabId(tabId); - - // Sync - let response; - switch (request.what) { - case 'contentScriptHasLocalStorage': - response = - contentScriptLocalStorageHandler(tabId, request.originURL); - break; - case 'lookupBlockedCollapsibles': - response = lookupBlockedCollapsibles(tabId, request); - break; - case 'mustRenderNoscriptTags?': - if (tabContext === null) { - break; - } - - response = - ηm.tMatrix.mustBlock(rootHostname, rootHostname, 'script') - && ηm.tMatrix.evaluateSwitchZ('noscript-spoof', rootHostname); - - if (pageStore !== null) { - pageStore.hasNoscriptTags = true; - } - - // https://github.com/gorhill/uMatrix/issues/225 - // A good place to force an update of the page title, as - // at this point the DOM has been loaded. - ηm.updateTitle(tabId); - break; - case 'securityPolicyViolation': - if (request.directive === 'worker-src') { - let url = UriTools.hostnameFromURI(request.blockedURI) !== '' ? - request.blockedURI : - request.documentURI; - - if (pageStore !== null) { - pageStore.hasWebWorkers = true; - pageStore.recordRequest('script', url, request.blocked); - } - - if (tabContext !== null) { - ηm.logger.writeOne(tabId, 'net', rootHostname, - url, 'worker', request.blocked); - } - } else if (request.directive === 'script-src') { - foundInlineCode(tabId, pageStore, request, 'script'); - } else if (request.directive === 'style-src') { - foundInlineCode(tabId, pageStore, request, 'css'); - } - break; - case 'shutdown?': - if (tabContext !== null) { - response = - ηm.tMatrix.evaluateSwitchZ('matrixb-off', rootHostname); - } - break; - default: - return vAPI.messaging.UNHANDLED; - } - - callback(response); - }; - - vAPI.messaging.listen('contentscript.js', onMessage); -})(); - -// cloud-ui.js -(function () { - - let ηm = ηMatrix; - - let onMessage = function (request, sender, callback) { - // Async - switch (request.what) { - case 'cloudGetOptions': - vAPI.cloud.getOptions(function (options) { - options.enabled = ηm.userSettings.cloudStorageEnabled === true; - callback(options); - }); - return; - case 'cloudSetOptions': - vAPI.cloud.setOptions(request.options, callback); - return; - case 'cloudPull': - return vAPI.cloud.pull(request.datakey, callback); - case 'cloudPush': - return vAPI.cloud.push(request.datakey, request.data, callback); - default: - break; - } - - // Sync - let response; - switch (request.what) { - // For when cloud storage is disabled. - case 'cloudPull': - // fallthrough - case 'cloudPush': - break; - default: - return vAPI.messaging.UNHANDLED; - } - - callback(response); - }; - - vAPI.messaging.listen('cloud-ui.js', onMessage); -})(); - -// user-rules.js -(function () { - - let ηm = ηMatrix; - - let onMessage = function (request, sender, callback) { - // Async - switch (request.what) { - default: - break; + addMethod('setUserRules', function (request, sender, cb) { + if (typeof request.temporaryRules === 'string') { + ηm.tMatrix.fromString(request.temporaryRules); } - - // Sync - let response; - switch (request.what) { - case 'getUserRules': - response = { - temporaryRules: ηm.tMatrix.toString(), - permanentRules: ηm.pMatrix.toString(), - }; - break; - case 'setUserRules': - if (typeof request.temporaryRules === 'string') { - ηm.tMatrix.fromString(request.temporaryRules); - } - if (typeof request.permanentRules === 'string') { - ηm.pMatrix.fromString(request.permanentRules); - ηm.saveMatrix(); - } - response = { - temporaryRules: ηm.tMatrix.toString(), - permanentRules: ηm.pMatrix.toString(), - }; - break; - default: - return vAPI.messaging.UNHANDLED; - } - - callback(response); - }; - - vAPI.messaging.listen('user-rules.js', onMessage); -})(); - -// hosts-files.js -(function () { - - let ηm = ηMatrix; - - let prepEntries = function (entries) { - let ηmuri = UriTools; - let entry; - for (let k in entries) { - if (entries.hasOwnProperty(k) === false) { - continue; - } - - entry = entries[k]; - - if (typeof entry.homeURL === 'string') { - entry.homeHostname = ηmuri.hostnameFromURI(entry.homeURL); - entry.homeDomain = ηmuri.domainFromHostname(entry.homeHostname); - } + if (typeof request.permanentRules === 'string') { + ηm.pMatrix.fromString(request.permanentRules); + ηm.saveMatrix(); } - }; - - let getLists = function (callback) { - let r = { - autoUpdate: ηm.userSettings.autoUpdate, - available: null, - cache: null, - current: ηm.liveHostsFiles, - blockedHostnameCount: ηm.ubiquitousBlacklist.count, - }; - - let onMetadataReady = function (entries) { - r.cache = entries; - prepEntries(r.cache); - callback(r); - }; - - let onAvailableHostsFilesReady = function (lists) { - r.available = lists; - prepEntries(r.available); - ηm.assets.metadata(onMetadataReady); + return { + temporaryRules: ηm.tMatrix.toString(), + permanentRules: ηm.pMatrix.toString(), }; - - ηm.getAvailableHostsFiles(onAvailableHostsFilesReady); - }; - - let onMessage = function (request, sender, callback) { - let ηm = ηMatrix; - - // Async - switch (request.what) { - case 'getLists': - return getLists(callback); - default: - break; - } - - // Sync - let response; - switch (request.what) { - case 'purgeCache': - ηm.assets.purge(request.assetKey); - ηm.assets.remove('compiled/' + request.assetKey); - break; - case 'purgeAllCaches': - if (request.hard) { - ηm.assets.remove(/./); - } else { - ηm.assets.purge(/./, 'public_suffix_list.dat'); - } - break; - default: - return vAPI.messaging.UNHANDLED; + }); + + vAPI.messaging.listen('user-rules.js', dispatchMethod); + + // hosts-files.js begins here + addMethod('getLists', function (request, sender, cb) { + let prepare = function (entries) { + for (let e in entries) { + if (entries.hasOwnProperty(e) === false) { + continue; + } + + let entry = entries[e]; + + if (typeof entry.homeURL === 'string') { + let homehost = UriTools.hostnameFromURI(entry.homeURL); + entry.homeHostname = homehost + entry.homeDomain = UriTools.domainFromHostname(homehost); + } + } + }; + + let getLists = function (cb) { + let r = { + autoUpdate: ηm.userSettings.autoUpdate, + available: null, + cache: null, + current: ηm.liveHostsFiles, + blockedHostnameCount: ηm.ubiquitousBlacklist.count, + }; + + let onMetadataReady = function (entries) { + r.cache = entries; + prepare(r.cache); + cb(r); + }; + + let onHostsReady = function (lists) { + r.available = lists; + prepare(r.available); + ηm.assets.metadata(onMetadataReady); + }; + + ηm.getAvailableHostsFiles(onHostsReady); + }; + + return getLists(cb); + }, true); + + addMethod('purgeCache', function (request, sender, cb) { + ηm.assets.purge(request.assetKey); + ηm.assets.remove('compiled/' + request.assetKey); + return undefined; + }); + + addMethod('purgeAllCaches', function (request, sender, cb) { + if (request.hard) { + ηm.assets.remove(/./); + } else { + ηm.assets.purge(/./, 'public_suffix_list.dat'); } - - callback(response); - }; - - vAPI.messaging.listen('hosts-files.js', onMessage); -})(); - -// about.js -(function () { - - let ηm = ηMatrix; - - let restoreUserData = function (userData) { - let countdown = 4; - let onCountdown = function () { - countdown -= 1; - if (countdown === 0) { - vAPI.app.restart(); - } + return undefined; + }); + + vAPI.messaging.listen('hosts-files.js', dispatchMethod); + + // about.js begins here + addMethod('getAllUserData', function (request, sender, cb) { + return { + app: vAPI.app.name, + version: vAPI.app.version, + when: Date.now(), + settings: ηm.userSettings, + rules: ηm.pMatrix.toString(), + hostsFiles: ηm.liveHostsFiles, + rawSettings: ηm.rawSettings, }; + }); - let onAllRemoved = function () { - vAPI.storage.set(userData.settings, onCountdown); - vAPI.storage.set({ - userMatrix: userData.rules, - }, onCountdown); - vAPI.storage.set({ - liveHostsFiles: userData.hostsFiles, - }, onCountdown); - - if (userData.rawSettings instanceof Object) { - ηMatrix.saveRawSettings(userData.rawSettings, onCountdown); - } + addMethod('getSomeStats', function (request, sender, cb) { + return { + version: vAPI.app.version, + storageUsed: ηm.storageUsed }; - - // If we are going to restore all, might as well wipe out - // clean local storage - ηm.XAL.keyvalRemoveAll(onAllRemoved); - }; - - let resetUserData = function () { - let onAllRemoved = function () { + }); + + addMethod('restoreAllUserData', function (request, sender, cb) { + let restoreData = function (data) { + let countdown = 4; + let onCountdown = function () { + countdown -= 1; + if (countdown === 0) { + vAPI.app.restart(); + } + }; + + let onAllRemoved = function () { + vAPI.storage.set(userData.settings, onCountdown); + vAPI.storage.set({ + userMatrix: userData.rules, + }, onCountdown); + vAPI.storage.set({ + liveHostsFiles: userData.hostsFiles, + }, onCountdown); + + if (userData.rawSettings instanceof Object) { + ηMatrix.saveRawSettings(userData.rawSettings, onCountdown); + } + }; + + // If we are going to restore all, might as well wipe out + // clean local storage + ηm.XAL.keyvalRemoveAll(onAllRemoved); + }; + + restoreUserData(request.userData); + return undefined; + }); + + addMethod('resetAllUserData', function (request, sender, cb) { + let onAllRemoved = function () { vAPI.app.restart(); - }; - ηm.XAL.keyvalRemoveAll(onAllRemoved); - }; + }; - let onMessage = function (request, sender, callback) { - // Async - switch (request.what) { - default: - break; - } + ηm.XAL.keyvalRemoveAll(onAllRemoved); + return undefined; + }); - // Sync - let response; - switch (request.what) { - case 'getAllUserData': - response = { - app: vAPI.app.name, - version: vAPI.app.version, - when: Date.now(), - settings: ηm.userSettings, - rules: ηm.pMatrix.toString(), - hostsFiles: ηm.liveHostsFiles, - rawSettings: ηm.rawSettings, - }; - break; - case 'getSomeStats': - response = { - version: vAPI.app.version, - storageUsed: ηm.storageUsed - }; - break; - case 'restoreAllUserData': - restoreUserData(request.userData); - break; - case 'resetAllUserData': - resetUserData(); - break; - default: - return vAPI.messaging.UNHANDLED; - } + vAPI.messaging.listen('about.js', dispatchMethod); - callback(response); - }; + // logger-ui.js begins here + let loggerURL = vAPI.getURL('logger-ui.html'); - vAPI.messaging.listen('about.js', onMessage); -})(); + addMethod('readMany', function (request, sender, cb) { + if (ηm.logger.ownerId !== undefined + && request.ownerId !== ηm.logger.ownerId) { + return { + unavailable: true, + }; + } -// logger-ui.js -(function () { + let tabIds = {}; + for (let id in ηm.pageStores) { + let store = ηm.pageStoreFromTabId(tabId); - let ηm = ηMatrix; - let loggerURL = vAPI.getURL('logger-ui.html'); + if (store === null) { + continue; + } + if (store.rawUrl.startsWith(loggerURL)) { + continue; + } - let onMessage = function (request, sender, callback) { - // Async - switch (request.what) { - default: - break; + tabIds[id] = store.title || store.rawUrl; } - // Sync - let response; - switch (request.what) { - case 'readMany': - if (ηm.logger.ownerId !== undefined - && request.ownerId !== ηm.logger.ownerId) { - response = { - unavailable: true, - }; - break; - } - let tabIds = {}; - for (let tabId in ηm.pageStores) { - let pageStore = ηm.pageStoreFromTabId(tabId); - - if (pageStore === null) { - continue; - } - if (pageStore.rawUrl.startsWith(loggerURL)) { - continue; - } - - tabIds[tabId] = pageStore.title || pageStore.rawUrl; - } - - response = { - colorBlind: false, - entries: ηm.logger.readAll(request.ownerId), - maxLoggedRequests: ηm.userSettings.maxLoggedRequests, - noTabId: vAPI.noTabId, - tabIds: tabIds, - tabIdsToken: ηm.pageStoresToken, - }; - break; - case 'releaseView': - if (request.ownerId === ηm.logger.ownerId) { - ηm.logger.ownerId = undefined; - } - break; - default: - return vAPI.messaging.UNHANDLED; - } + return { + colorBlind: false, + entries: ηm.logger.readAll(request.ownerId), + maxLoggedRequests: ηm.userSettings.maxLoggedRequests, + noTabId: vAPI.noTabId, + tabIds: tabIds, + tabIdsToken: ηm.pageStoresToken, + }; + }); - callback(response); - }; + addMethod('releaseView', function (request, sender, cb) { + if (request.ownerId === ηm.logger.ownerId) { + ηm.logger.ownerId = undefined; + } + return undefined; + }); - vAPI.messaging.listen('logger-ui.js', onMessage); + vAPI.messaging.listen('logger-ui.js', dispatchMethod); })(); |