diff --git a/src/js/contentscript-no-inline-script.js b/src/js/contentscript-no-inline-script.js
new file mode 100644
index 0000000..746d744
--- /dev/null
+++ b/src/js/contentscript-no-inline-script.js
@@ -0,0 +1,62 @@
+/*******************************************************************************
+
+ uMatrix - a Chromium browser extension to black/white list requests.
+ Copyright (C) 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+// The idea of using to enforce CSP directive has been
+// borrowed from NoScript:
+// https://github.com/hackademix/noscript/commit/6e80d3f130773fc9a9123c5c4c2e97d63e90fa2a
+
+(function() {
+ let html = document.documentElement;
+ if ( html instanceof HTMLElement === false ) { return; }
+
+ let meta;
+ try {
+ meta = document.createElement('meta');
+ } catch(ex) {
+ }
+ if ( meta === undefined ) { return; }
+ meta.setAttribute('http-equiv', 'content-security-policy');
+ meta.setAttribute('content', "script-src 'unsafe-eval' blob: *");
+
+ // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-security-policy
+ //
+ // Only a head element can be parent:
+ // > If the meta element is not a child of a head element, return.
+ //
+ // The CSP directive is enforced as soon as the meta tag is inserted:
+ // > Enforce the policy policy.
+ let head = document.head,
+ parent = head;
+ if ( parent === null ) {
+ parent = document.createElement('head');
+ html.appendChild(parent);
+ }
+ parent.appendChild(meta);
+
+ // Restore DOM to its original state.
+ if ( head === null ) {
+ html.removeChild(parent);
+ } else {
+ parent.removeChild(meta);
+ }
+})();
diff --git a/src/js/contentscript-no-workers.js b/src/js/contentscript-no-workers.js
new file mode 100644
index 0000000..5f951a8
--- /dev/null
+++ b/src/js/contentscript-no-workers.js
@@ -0,0 +1,62 @@
+/*******************************************************************************
+
+ uMatrix - a Chromium browser extension to black/white list requests.
+ Copyright (C) 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
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ 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://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+// The idea of using to enforce CSP directive has been
+// borrowed from NoScript:
+// https://github.com/hackademix/noscript/commit/6e80d3f130773fc9a9123c5c4c2e97d63e90fa2a
+
+(function() {
+ let html = document.documentElement;
+ if ( html instanceof HTMLElement === false ) { return; }
+
+ let meta;
+ try {
+ meta = document.createElement('meta');
+ } catch(ex) {
+ }
+ if ( meta === undefined ) { return; }
+ meta.setAttribute('http-equiv', 'content-security-policy');
+ meta.setAttribute('content', "worker-src 'none'");
+
+ // https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-security-policy
+ //
+ // Only a head element can be parent:
+ // > If the meta element is not a child of a head element, return.
+ //
+ // The CSP directive is enforced as soon as the meta tag is inserted:
+ // > Enforce the policy policy.
+ let head = document.head,
+ parent = head;
+ if ( parent === null ) {
+ parent = document.createElement('head');
+ html.appendChild(parent);
+ }
+ parent.appendChild(meta);
+
+ // Restore DOM to its original state.
+ if ( head === null ) {
+ html.removeChild(parent);
+ } else {
+ parent.removeChild(meta);
+ }
+})();
diff --git a/src/js/matrix.js b/src/js/matrix.js
index 1472c1d..aaa223b 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-2018 Raymond Hill
+ uMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-present 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
@@ -199,6 +199,30 @@ Matrix.prototype.reset = function() {
this.rules = new Map();
this.rootValue = Matrix.RedIndirect;
this.modifiedTime = 0;
+ if ( this.modifyEventTimer !== undefined ) {
+ clearTimeout(this.modifyEventTimer);
+ }
+ this.modifyEventTimer = undefined;
+ this.modified();
+};
+
+/******************************************************************************/
+
+Matrix.prototype.modified = function() {
+ this.modifiedTime = Date.now();
+ if ( this.modifyEventTimer !== undefined ) { return; }
+ this.modifyEventTimer = vAPI.setTimeout(
+ ( ) => {
+ this.modifyEventTimer = undefined;
+ window.dispatchEvent(
+ new CustomEvent(
+ 'matrixRulesetChange',
+ { detail: this }
+ )
+ );
+ },
+ 149
+ );
};
/******************************************************************************/
@@ -242,7 +266,7 @@ Matrix.prototype.assign = function(other) {
for ( entry of other.switches ) {
this.switches.set(entry[0], entry[1]);
}
- this.modifiedTime = other.modifiedTime;
+ this.modified();
return this;
};
@@ -268,7 +292,7 @@ Matrix.prototype.setSwitch = function(switchName, srcHostname, newVal) {
} else {
this.switches.set(srcHostname, bits);
}
- this.modifiedTime = Date.now();
+ this.modified();
return true;
};
@@ -290,7 +314,7 @@ Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) {
} else {
this.rules.set(k, newBitmap);
}
- this.modifiedTime = Date.now();
+ this.modified();
return true;
};
@@ -531,7 +555,7 @@ Matrix.prototype.setSwitchZ = function(switchName, srcHostname, newState) {
} else {
this.switches.set(srcHostname, bits);
}
- this.modifiedTime = Date.now();
+ this.modified();
state = this.evaluateSwitchZ(switchName, srcHostname);
if ( state === newState ) {
return true;
@@ -645,7 +669,7 @@ Matrix.prototype.fromArray = function(lines, append) {
if ( append !== true ) {
this.assign(matrix);
}
- this.modifiedTime = Date.now();
+ this.modified();
};
Matrix.prototype.toArray = function() {
@@ -707,7 +731,7 @@ Matrix.prototype.fromString = function(text, append) {
this.assign(matrix);
}
- this.modifiedTime = Date.now();
+ this.modified();
};
Matrix.prototype.toString = function() {
@@ -750,7 +774,7 @@ Matrix.prototype.fromSelfie = function(selfie) {
if ( selfie.version !== selfieVersion ) { return false; }
this.switches = new Map(selfie.switches);
this.rules = new Map(selfie.rules);
- this.modifiedTime = Date.now();
+ this.modified();
return true;
};
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 34ddf3e..9688d71 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
- Copyright (C) 2014-2018 Raymond Hill
+ Copyright (C) 2014-present 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
@@ -229,6 +229,13 @@ var matrixSnapshot = function(pageStore, details) {
let µmuri = µm.URI;
let anyIndex = headerIndices.get('*');
+ // Ensure that the current scope is also reported in the matrix. This may
+ // not be the case for documents which are fetched without going through
+ // our webRequest listener (ex. `file:`).
+ if ( pageStore.hostnameTypeCells.has(r.hostname + ' doc') === false ) {
+ pageStore.hostnameTypeCells.set(r.hostname + ' doc', new Set([ 0 ]));
+ }
+
for ( let entry of pageStore.hostnameTypeCells ) {
let pos = entry[0].indexOf(' ');
let reqHostname = entry[0].slice(0, pos);
diff --git a/src/js/traffic.js b/src/js/traffic.js
index eb62c50..461f575 100644
--- a/src/js/traffic.js
+++ b/src/js/traffic.js
@@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
- Copyright (C) 2014-2018 Raymond Hill
+ Copyright (C) 2014-present 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
@@ -483,6 +483,97 @@ vAPI.net.onHeadersReceived = {
callback: onHeadersReceived
};
+/*******************************************************************************
+
+ Use a `http-equiv` `meta` tag to enforce CSP directives for documents
+ which protocol is `file:` (which do not cause our webRequest.onHeadersReceived
+ handler to be called).
+
+ Idea borrowed from NoScript:
+ https://github.com/hackademix/noscript/commit/6e80d3f130773fc9a9123c5c4c2e97d63e90fa2a
+
+**/
+
+(function() {
+ if (
+ typeof self.browser !== 'object' ||
+ typeof browser.contentScripts !== 'object'
+ ) {
+ return;
+ }
+
+ let csRules = [
+ {
+ name: 'script',
+ file: '/js/contentscript-no-inline-script.js',
+ pending: undefined,
+ registered: undefined,
+ mustRegister: false
+ },
+ ];
+
+ let csSwitches = [
+ {
+ name: 'no-workers',
+ file: '/js/contentscript-no-workers.js',
+ pending: undefined,
+ registered: undefined,
+ mustRegister: false
+ },
+ ];
+
+ let register = function(entry) {
+ if ( entry.pending !== undefined ) { return; }
+ entry.pending = browser.contentScripts.register({
+ js: [ { file: entry.file } ],
+ matches: [ 'file:///*' ],
+ runAt: 'document_start'
+ }).then(
+ result => {
+ if ( entry.mustRegister ) {
+ entry.registered = result;
+ }
+ entry.pending = undefined;
+ },
+ ( ) => {
+ entry.registered = undefined;
+ entry.pending = undefined;
+ }
+ );
+ };
+
+ let unregister = function(entry) {
+ if ( entry.registered === undefined ) { return; }
+ entry.registered.unregister();
+ entry.registered = undefined;
+ };
+
+ let handler = function(ev) {
+ let matrix = ev && ev.detail;
+ if ( matrix !== µMatrix.tMatrix ) { return; }
+ for ( let cs of csRules ) {
+ cs.mustRegister = matrix.mustBlock('file-scheme', 'file-scheme', cs.name);
+ if ( cs.mustRegister === (cs.registered !== undefined) ) { continue; }
+ if ( cs.mustRegister ) {
+ register(cs);
+ } else {
+ unregister(cs);
+ }
+ }
+ for ( let cs of csSwitches ) {
+ cs.mustRegister = matrix.evaluateSwitchZ(cs.name, 'file-scheme');
+ if ( cs.mustRegister === (cs.registered !== undefined) ) { continue; }
+ if ( cs.mustRegister ) {
+ register(cs);
+ } else {
+ unregister(cs);
+ }
+ }
+ };
+
+ window.addEventListener('matrixRulesetChange', handler);
+})();
+
/******************************************************************************/
var start = function() {