diff --git a/src/css/fonts/fontawesome-webfont.ttf b/src/css/fonts/fontawesome-webfont.ttf index 5cd6cff..96a3639 100755 Binary files a/src/css/fonts/fontawesome-webfont.ttf and b/src/css/fonts/fontawesome-webfont.ttf differ diff --git a/src/css/popup.css b/src/css/popup.css index a71e52d..b893460 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -66,9 +66,6 @@ body .toolbar button:hover { body .toolbar button.disabled { color: #ccc; } -body .toolbar button.switch.disabled { - color: #a00; - } body .toolbar button.fa { font: 1.75em 'FontAwesome'; min-width: 1.1em; @@ -82,18 +79,38 @@ body.tScopeDomain .scopeRel:not(.disabled) { body.tScopeSite .scopeRel:not(.disabled) { color: #48c; } +#mtxSwitch_matrix-off.switchTrue { + color: #a00; + } +#mtxSwitches > li { + color: #888; + } +#mtxSwitches > li.switchTrue { + color: #000; + } +#mtxSwitches > li > span:before { + content: '\f204'; + font: 120% FontAwesome; + padding-right: 0.5em; + } +#mtxSwitches > li.switchTrue > span:before { + color: #000; + content: '\f205'; + font: 120% FontAwesome; + padding-right: 0.5em; + } .dropdown-menu { margin: 0; border: 0; + display: none; + font-size: 110%; padding: 3px 0 0 0; position: absolute; - z-index: 50; - font-size: 110%; - display: none; white-space: normal; + z-index: 50; } .dropdown-menu > ul { margin: 0; @@ -105,26 +122,24 @@ body.tScopeSite .scopeRel:not(.disabled) { list-style-type: none; } .dropdown-menu > ul > li.dropdown-menu-entry { - margin: 0; border: 0; - padding: 4px 0.5em; color: black; cursor: pointer; + margin: 0; + padding: 4px 0.5em; + white-space: nowrap; } .dropdown-menu > ul > li.dropdown-menu-entry:hover { background: #eee; } .dropdown-menu > ul > li.dropdown-menu-entry-divider { - margin: 0.5em 0; border-top: 1px solid #ccc; + margin: 0.5em 0; } .dropdown-menu > ul > li.dropdown-menu-entry > .fa { - margin-right: 0.5em; - font-size: 110%; color: #aaa; - } -.dropdown-menu { - display: none; + font-size: 110%; + margin-right: 0.5em; } .dropdown-menu.show { display: block; diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js index f19027e..a768a79 100644 --- a/src/js/contentscript-start.js +++ b/src/js/contentscript-start.js @@ -205,7 +205,11 @@ var injectNavigatorSpoofer = function(spoofedUserAgent) { } }; -messaging.ask({ what: 'getUserAgentReplaceStr' }, injectNavigatorSpoofer); +var requestDetails = { + what: 'getUserAgentReplaceStr', + hostname: window.location.hostname +}; +messaging.ask(requestDetails, injectNavigatorSpoofer); /******************************************************************************/ /******************************************************************************/ diff --git a/src/js/httpsb.js b/src/js/httpsb.js index 301f079..6ac76e7 100644 --- a/src/js/httpsb.js +++ b/src/js/httpsb.js @@ -26,11 +26,11 @@ (function() { var µm = µMatrix; µm.pMatrix = new µm.Matrix(); - µm.pMatrix.setSwitch('matrix-off', 'localhost', true); - µm.pMatrix.setSwitch('matrix-off', 'chrome-extension-scheme', true); - µm.pMatrix.setSwitch('matrix-off', 'chrome-scheme', true); - µm.pMatrix.setSwitch('matrix-off', µm.behindTheSceneScope, true); - µm.pMatrix.setSwitch('matrix-off', 'opera-scheme', true); + µm.pMatrix.setSwitch('matrix-off', 'localhost', 1); + µm.pMatrix.setSwitch('matrix-off', 'chrome-extension-scheme', 1); + µm.pMatrix.setSwitch('matrix-off', 'chrome-scheme', 1); + µm.pMatrix.setSwitch('matrix-off', µm.behindTheSceneScope, 1); + µm.pMatrix.setSwitch('matrix-off', 'opera-scheme', 1); µm.pMatrix.setCell('*', '*', '*', µm.Matrix.Red); µm.pMatrix.setCell('*', '*', 'doc', µm.Matrix.Green); µm.pMatrix.setCell('*', '*', 'css', µm.Matrix.Green); diff --git a/src/js/matrix.js b/src/js/matrix.js index 12fc337..4efadb8 100644 --- a/src/js/matrix.js +++ b/src/js/matrix.js @@ -81,17 +81,22 @@ var nameToStateMap = { 'inherit': 3 }; -var nameToSwitchStateMap = { - 'true': true, - 'false': false, - 'on': false, // backward compatibility - 'off': true // backward compatibility -}; - var switchBitOffsets = { 'matrix-off': 0, 'https-strict': 2, - 'ua-spoof-off': 4 + 'ua-spoof': 4 +}; + +var switchStateToNameMap = { + '1': 'true', + '2': 'false' +}; + +var nameToSwitchStateMap = { + 'true': 1, + 'false': 2, + 'on': 2, // backward compatibility + 'off': 1 // backward compatibility }; /******************************************************************************/ @@ -116,6 +121,25 @@ Matrix.getColumnHeaders = function() { /******************************************************************************/ +var switchNames = (function() { + var out = {}; + for ( var switchName in switchBitOffsets ) { + if ( switchBitOffsets.hasOwnProperty(switchName) === false ) { + continue; + } + out[switchName] = true; + } + return out; +})(); + +/******************************************************************************/ + +Matrix.getSwitchNames = function() { + return switchNames; +}; + +/******************************************************************************/ + // For performance purpose, as simple tests as possible var reHostnameVeryCoarse = /[g-z_-]/; var reIPv4VeryCoarse = /\.\d+$/; @@ -232,17 +256,22 @@ Matrix.prototype.assign = function(other) { // If value is undefined, the switch is removed -Matrix.prototype.setSwitch = function(switchName, srcHostname, newState) { +Matrix.prototype.setSwitch = function(switchName, srcHostname, newVal) { var bitOffset = switchBitOffsets[switchName]; if ( bitOffset === undefined ) { return false; } - var state = this.evaluateSwitch(switchName, srcHostname); - if ( newState === state ) { + if ( newVal === this.evaluateSwitch(switchName, srcHostname) ) { return false; } var bits = this.switches[srcHostname] || 0; - this.switches[srcHostname] = (bits & ~(3 << bitOffset)) | ((newState ? 1 : 2) << bitOffset); + bits &= ~(3 << bitOffset); + bits |= newVal << bitOffset; + if ( bits === 0 ) { + delete this.switches[srcHostname]; + } else { + this.switches[srcHostname] = bits; + } return true; }; @@ -508,13 +537,13 @@ Matrix.prototype.setSwitchZ = function(switchName, srcHostname, newState) { Matrix.prototype.evaluateSwitch = function(switchName, srcHostname) { var bits = this.switches[srcHostname] || 0; if ( bits === 0 ) { - return false; + return 0; } var bitOffset = switchBitOffsets[switchName]; if ( bitOffset === undefined ) { - return false; + return 0; } - return ((bits >> bitOffset) & 3) === 1; + return (bits >> bitOffset) & 3; }; /******************************************************************************/ @@ -613,10 +642,10 @@ Matrix.prototype.toString = function() { continue; } val = this.evaluateSwitch(switchName, srcHostname); - if ( val === false ) { + if ( val === 0 ) { continue; } - out.push(switchName + ': ' + srcHostname + ' ' + val); + out.push(switchName + ': ' + srcHostname + ' ' + switchStateToNameMap[val]); } } return out.join('\n'); diff --git a/src/js/messaging-handlers.js b/src/js/messaging-handlers.js index 7433bd7..7673529 100644 --- a/src/js/messaging-handlers.js +++ b/src/js/messaging-handlers.js @@ -72,8 +72,8 @@ var matrixSnapshot = function(details) { blockedCount: 0, scope: '*', headers: µm.Matrix.getColumnHeaders(), - tSwitch: false, - pSwitch: false, + tSwitches: {}, + pSwitches: {}, rows: {}, rowCount: 0, diff: [], @@ -115,8 +115,14 @@ var matrixSnapshot = function(details) { r.scope = r.domain; } - r.tSwitch = µm.tMatrix.evaluateSwitchZ('matrix-off', r.scope); - r.pSwitch = µm.pMatrix.evaluateSwitchZ('matrix-off', r.scope); + var switchNames = µm.Matrix.getSwitchNames(); + for ( var switchName in switchNames ) { + if ( switchNames.hasOwnProperty(switchName) === false ) { + continue; + } + 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, '*', '*'); @@ -210,9 +216,9 @@ var onMessage = function(request, sender, callback) { case 'toggleMatrixSwitch': µm.tMatrix.setSwitchZ( - 'matrix-off', + request.switchName, request.srcHostname, - !µm.tMatrix.evaluateSwitchZ('matrix-off', request.srcHostname) + µm.tMatrix.evaluateSwitchZ(request.switchName, request.srcHostname) === false ); break; @@ -272,6 +278,10 @@ var onMessage = function(request, sender, callback) { (function() { +var µm = µMatrix; + +/******************************************************************************/ + var contentScriptSummaryHandler = function(details, sender) { // TODO: Investigate "Error in response to tabs.executeScript: TypeError: // Cannot read property 'locationURL' of null" (2013-11-12). When can this @@ -279,7 +289,6 @@ var contentScriptSummaryHandler = function(details, sender) { if ( !details || !details.locationURL ) { return; } - var µm = µMatrix; var pageURL = µm.pageUrlFromTabId(sender.tab.id); var pageStats = µm.pageStatsFromPageUrl(pageURL); var µmuri = µm.URI.set(details.locationURL); @@ -327,8 +336,9 @@ var contentScriptSummaryHandler = function(details, sender) { µm.onPageLoadCompleted(pageURL); }; +/******************************************************************************/ + var contentScriptLocalStorageHandler = function(pageURL) { - var µm = µMatrix; var µmuri = µm.URI.set(pageURL); var response = µm.mustBlock(µm.scopeFromURL(pageURL), µmuri.hostname, 'cookie'); µm.recordFromPageUrl( @@ -344,6 +354,8 @@ var contentScriptLocalStorageHandler = function(pageURL) { return response; }; +/******************************************************************************/ + var onMessage = function(request, sender, callback) { // Async switch ( request.what ) { @@ -365,21 +377,22 @@ var onMessage = function(request, sender, callback) { case 'checkScriptBlacklisted': response = { - scriptBlacklisted: µMatrix.mustBlock( - µMatrix.scopeFromURL(request.url), - µMatrix.hostnameFromURL(request.url), + scriptBlacklisted: µm.mustBlock( + µm.scopeFromURL(request.url), + µm.hostnameFromURL(request.url), 'script' - ) - }; + ) + }; break; case 'getUserAgentReplaceStr': - response = µMatrix.userSettings.spoofUserAgent ? µMatrix.userAgentReplaceStr : undefined; + response = µm.tMatrix.evaluateSwitchZ('ua-spoof', request.hostname) ? + µm.userAgentReplaceStr : + undefined; break; - default: - return µMatrix.messaging.defaultHandler(request, sender, callback); + return µm.messaging.defaultHandler(request, sender, callback); } callback(response); @@ -388,6 +401,8 @@ var onMessage = function(request, sender, callback) { µMatrix.messaging.listen('contentscript-start.js', onMessage); µMatrix.messaging.listen('contentscript-end.js', onMessage); +/******************************************************************************/ + })(); /******************************************************************************/ diff --git a/src/js/popup.js b/src/js/popup.js index 177a2b6..66c0f15 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -1016,19 +1016,31 @@ function updateScopeCell() { /******************************************************************************/ -function updateMtxbutton() { - var masterSwitch = matrixSnapshot.tSwitch; +function updateMatrixSwitches() { + var switches = matrixSnapshot.tSwitches; + for ( var switchName in switches ) { + if ( switches.hasOwnProperty(switchName) === false ) { + continue; + } + uDom('#mtxSwitch_' + switchName).toggleClass('switchTrue', switches[switchName]); + + } var count = matrixSnapshot.blockedCount; - var button = uDom('#buttonMtxFiltering'); - button.toggleClass('disabled', masterSwitch); + var button = uDom('#mtxSwitch_matrix-off'); button.descendants('span.badge').text(count.toLocaleString()); button.attr('data-tip', button.attr('data-tip').replace('{{count}}', count)); - uDom('body').toggleClass('powerOff', masterSwitch); + uDom('body').toggleClass('powerOff', switches['matrix-off']); } -function toggleMtxFiltering() { +function toggleMatrixSwitch() { + var pos = this.id.indexOf('_'); + if ( pos === -1 ) { + return; + } + var switchName = this.id.slice(pos + 1); var request = { what: 'toggleMatrixSwitch', + switchName: switchName, srcHostname: matrixSnapshot.scope }; messaging.ask(request, updateMatrixSnapshot); @@ -1078,7 +1090,7 @@ function revertMatrix() { function updateMatrixButtons() { updateScopeCell(); - updateMtxbutton(); + updateMatrixSwitches(); updatePersistButton(); } @@ -1217,7 +1229,7 @@ uDom.onLoad(function() { uDom('#scopeKeyGlobal').on('click', selectGlobalScope); uDom('#scopeKeyDomain').on('click', selectDomainScope); uDom('#scopeKeySite').on('click', selectSiteScope); - uDom('#buttonMtxFiltering').on('click', toggleMtxFiltering); + uDom('[id^="mtxSwitch_"]').on('click', toggleMatrixSwitch); uDom('#buttonPersist').on('click', persistMatrix); uDom('#buttonRevertScope').on('click', revertMatrix); diff --git a/src/js/traffic.js b/src/js/traffic.js index 377de87..c0e2732 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -474,9 +474,7 @@ var onBeforeSendHeadersHandler = function(details) { } // TODO: move the master ua-spoofing switch into the matrix. - var mustSpoof = µm.userSettings.spoofUserAgent && - µm.tMatrix.evaluateSwitchZ('ua-spoof-off', pageStore.pageHostname) === false; - if ( mustSpoof ) { + if ( µm.tMatrix.evaluateSwitchZ('ua-spoof', pageStore.pageHostname) ) { changed = foilUserAgent(µm, details) || changed; // https://github.com/gorhill/httpswitchboard/issues/252 // To avoid potential mismatch between the user agent from HTTP headers @@ -715,7 +713,7 @@ var onMainDocHeadersReceived = function(details) { // Enforce strict HTTPS? if ( requestScheme === 'https' && µm.tMatrix.evaluateSwitchZ('https-strict', pageStats.pageHostname) ) { - csp += "default-src https: 'unsafe-inline' 'unsafe-eval'"; + csp += "default-src https: data: 'unsafe-inline' 'unsafe-eval'"; } // https://github.com/gorhill/httpswitchboard/issues/181 diff --git a/src/popup.html b/src/popup.html index a2d976e..7a6d4ed 100644 --- a/src/popup.html +++ b/src/popup.html @@ -28,7 +28,18 @@
- + +