diff --git a/src/js/matrix.js b/src/js/matrix.js index 641c9e3..5aa78aa 100644 --- a/src/js/matrix.js +++ b/src/js/matrix.js @@ -1,7 +1,7 @@ /******************************************************************************* uMatrix - a Chromium browser extension to black/white list requests. - Copyright (C) 2014-2017 Raymond Hill + Copyright (C) 2014-2018 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +20,6 @@ */ /* global punycode */ -/* jshint bitwise: false */ 'use strict'; @@ -31,13 +30,11 @@ /******************************************************************************/ var µm = µMatrix; -var magicId = 'axyorpwxtmnf'; -var uniqueIdGenerator = 1; +var selfieVersion = 1; /******************************************************************************/ var Matrix = function() { - this.id = uniqueIdGenerator++; this.reset(); this.sourceRegister = ''; this.decomposedSourceRegister = ['']; @@ -103,10 +100,10 @@ var switchStateToNameMap = new Map([ [ 2, 'false' ] ]); -var nameToSwitchStateMap = { - 'true': 1, - 'false': 2 -}; +var nameToSwitchStateMap = new Map([ + [ 'true', 1 ], + [ 'false', 2 ] +]); /******************************************************************************/ @@ -594,15 +591,13 @@ Matrix.prototype.extractAllSourceHostnames = (function() { /******************************************************************************/ -Matrix.prototype.toString = function() { - var out = []; - var rule, type, switchName, val; - var srcHostname, desHostname; - for ( rule of this.rules.keys() ) { - srcHostname = this.srcHostnameFromRule(rule); - desHostname = this.desHostnameFromRule(rule); - for ( type of typeBitOffsets.keys() ) { - val = this.evaluateCell(srcHostname, desHostname, type); +Matrix.prototype.toArray = function() { + let out = []; + for ( let rule of this.rules.keys() ) { + let srcHostname = this.srcHostnameFromRule(rule); + let desHostname = this.desHostnameFromRule(rule); + for ( let type of typeBitOffsets.keys() ) { + let val = this.evaluateCell(srcHostname, desHostname, type); if ( val === 0 ) { continue; } out.push( punycode.toUnicode(srcHostname) + ' ' + @@ -612,176 +607,140 @@ Matrix.prototype.toString = function() { ); } } - for ( srcHostname of this.switches.keys() ) { - for ( switchName of switchBitOffsets.keys() ) { - val = this.evaluateSwitch(switchName, srcHostname); + for ( let srcHostname of this.switches.keys() ) { + for ( let switchName of switchBitOffsets.keys() ) { + let val = this.evaluateSwitch(switchName, srcHostname); if ( val === 0 ) { continue; } - out.push(switchName + ': ' + srcHostname + ' ' + switchStateToNameMap.get(val)); + out.push( + switchName + ': ' + + srcHostname + ' ' + + switchStateToNameMap.get(val) + ); } } - return out.sort().join('\n'); + return out; +}; + +/******************************************************************************/ + +Matrix.prototype.fromArray = function(lines, append) { + let matrix = append === true ? this : new Matrix(); + for ( let line of lines ) { + matrix.fromLine(line); + } + if ( append !== true ) { + this.assign(matrix); + } + this.modifiedTime = Date.now(); +}; + +/******************************************************************************/ + +Matrix.prototype.toString = function() { + return this.toArray().join('\n'); }; /******************************************************************************/ Matrix.prototype.fromString = function(text, append) { - var matrix = append ? this : new Matrix(); - var textEnd = text.length; - var lineBeg = 0, lineEnd; - var line, pos; - var fields, fieldVal; - var switchName; - var srcHostname = ''; - var desHostname = ''; - var type, state; + let matrix = append === true ? this : new Matrix(); + let textEnd = text.length; + let lineBeg = 0; while ( lineBeg < textEnd ) { - lineEnd = text.indexOf('\n', lineBeg); - if ( lineEnd < 0 ) { + let lineEnd = text.indexOf('\n', lineBeg); + if ( lineEnd === -1 ) { lineEnd = text.indexOf('\r', lineBeg); - if ( lineEnd < 0 ) { + if ( lineEnd === -1 ) { lineEnd = textEnd; } } - line = text.slice(lineBeg, lineEnd).trim(); + let line = text.slice(lineBeg, lineEnd).trim(); lineBeg = lineEnd + 1; - pos = line.indexOf('# '); + let pos = line.indexOf('# '); if ( pos !== -1 ) { line = line.slice(0, pos).trim(); } - if ( line === '' ) { - continue; - } - - fields = line.split(/\s+/); - - // Less than 2 fields makes no sense - if ( fields.length < 2 ) { - continue; - } + if ( line === '' ) { continue; } - fieldVal = fields[0]; - - // Special directives: - - // title - pos = fieldVal.indexOf('title:'); - if ( pos !== -1 ) { - // TODO - continue; - } - - // Name - pos = fieldVal.indexOf('name:'); - if ( pos !== -1 ) { - // TODO - continue; - } + matrix.fromLine(line); + } - // Switch on/off + if ( append !== true ) { + this.assign(matrix); + } - // `switch:` srcHostname state - // state = [`true`, `false`] - switchName = ''; - if ( fieldVal === 'switch:' || fieldVal === 'matrix:' ) { - fieldVal = 'matrix-off:'; - } - pos = fieldVal.indexOf(':'); - if ( pos !== -1 ) { - switchName = fieldVal.slice(0, pos); - } - if ( switchBitOffsets.has(switchName) ) { - srcHostname = punycode.toASCII(fields[1]); + this.modifiedTime = Date.now(); +}; - // No state field: reject - fieldVal = fields[2]; - if ( fieldVal === null ) { - continue; - } - // Unknown state: reject - if ( nameToSwitchStateMap.hasOwnProperty(fieldVal) === false ) { - continue; - } +/******************************************************************************/ - // Backward compatibility: - // `chromium-behind-the-scene` is now `behind-the-scene` - if ( srcHostname === 'chromium-behind-the-scene' ) { - srcHostname = 'behind-the-scene'; - } +// https://github.com/gorhill/uMatrix/issues/759 +// Backward compatibility: 'plugin' => 'media' - matrix.setSwitch(switchName, srcHostname, nameToSwitchStateMap[fieldVal]); - continue; - } +Matrix.prototype.fromLine = function(line) { + let fields = line.split(/\s+/); + if ( fields.length < 3 ) { return false; } + let field0 = fields[0]; - // Unknown directive - if ( fieldVal.endsWith(':') ) { - continue; + // Switches + let pos = field0.indexOf(':'); + if ( pos !== -1 ) { + let switchName = field0.slice(0, pos); + let srcHostname = punycode.toASCII(fields[1]); + let state = fields[2]; + if ( + switchBitOffsets.has(switchName) === false || + nameToSwitchStateMap.has(state) === false + ) { + return false; } + this.setSwitch( + switchName, + srcHostname, + nameToSwitchStateMap.get(state) + ); + return true; + } - // Valid rule syntax: - - // srcHostname desHostname [type [state]] - // type = a valid request type - // state = [`block`, `allow`, `inherit`] - - // srcHostname desHostname type - // type = a valid request type - // state = `allow` - - // srcHostname desHostname - // type = `*` - // state = `allow` - - // Lines with invalid syntax silently ignored - - srcHostname = punycode.toASCII(fields[0]); - desHostname = punycode.toASCII(fields[1]); + // Rules + if ( fields.length < 4 ) { return false; } - fieldVal = fields[2]; + let srcHostname = punycode.toASCII(fields[0]); + let desHostname = punycode.toASCII(fields[1]); + let type = fields[2]; - if ( fieldVal !== undefined ) { - type = fieldVal; - // https://github.com/gorhill/uMatrix/issues/759 - // Backward compatibility. - if ( type === 'plugin' ) { - type = 'media'; - } - // Unknown type: reject - if ( typeBitOffsets.has(type) === false ) { - continue; - } - } else { - type = '*'; + if ( type !== undefined ) { + if ( type === 'plugin' ) { + type = 'media'; + } else if ( typeBitOffsets.has(type) === false ) { + return false; } + } else { + type = '*'; + } - fieldVal = fields[3]; + let state = fields[3]; - if ( fieldVal !== undefined ) { - // Unknown state: reject - if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) { - continue; - } - state = nameToStateMap[fieldVal]; - } else { - state = 2; + if ( state !== undefined ) { + if ( nameToStateMap.hasOwnProperty(state) === false ) { + return false; } - - matrix.setCell(srcHostname, desHostname, type, state); - } - - if ( !append ) { - this.assign(matrix); + state = nameToStateMap[state]; + } else { + state = 2; } - this.modifiedTime = Date.now(); + this.setCell(srcHostname, desHostname, type, state); + return true; }; /******************************************************************************/ Matrix.prototype.toSelfie = function() { return { - magicId: magicId, + version: selfieVersion, switches: Array.from(this.switches), rules: Array.from(this.rules) }; @@ -790,7 +749,7 @@ Matrix.prototype.toSelfie = function() { /******************************************************************************/ Matrix.prototype.fromSelfie = function(selfie) { - if ( selfie.magicId !== magicId ) { return false; } + if ( selfie.version !== selfieVersion ) { return false; } this.switches = new Map(selfie.switches); this.rules = new Map(selfie.rules); this.modifiedTime = Date.now(); diff --git a/src/js/messaging.js b/src/js/messaging.js index 4ec891a..70a9793 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -864,7 +864,7 @@ var onMessage = function(request, sender, callback) { version: vAPI.app.version, when: Date.now(), settings: µm.userSettings, - rules: µm.pMatrix.toString(), + rules: µm.pMatrix.toArray().sort(), rawSettings: µm.rawSettings }; break; diff --git a/src/js/storage.js b/src/js/storage.js index e82342e..22ac5e1 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -242,22 +242,27 @@ /******************************************************************************/ µMatrix.saveMatrix = function() { - µMatrix.XAL.keyvalSetOne('userMatrix', this.pMatrix.toString()); + vAPI.storage.set({ userMatrix: this.pMatrix.toArray() }); }; µMatrix.loadMatrix = function(callback) { if ( typeof callback !== 'function' ) { callback = this.noopFunc; } - var µm = this; - var onLoaded = function(bin) { - if ( bin.hasOwnProperty('userMatrix') ) { + let µm = this; + let onLoaded = function(bin) { + if ( bin instanceof Object === false ) { + return callback(); + } + if ( typeof bin.userMatrix === 'string' ) { µm.pMatrix.fromString(bin.userMatrix); - µm.tMatrix.assign(µm.pMatrix); - callback(); + } else if ( Array.isArray(bin.userMatrix) ) { + µm.pMatrix.fromArray(bin.userMatrix); } + µm.tMatrix.assign(µm.pMatrix); + callback(); }; - this.XAL.keyvalGetOne('userMatrix', onLoaded); + vAPI.storage.get('userMatrix', onLoaded); }; /******************************************************************************/