From 261548ff184926567a623e90df7954aeef842d59 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 30 Jun 2021 12:28:05 +0200 Subject: emply an sh-based build system; make some changes to blocking --- README.txt | 9 +- TODOS.org | 23 +- background/main.js | 262 ++++---- background/message_server.js | 41 +- background/page_actions_server.js | 264 ++++---- background/policy_injector.js | 114 ++-- background/settings_query.js | 204 +++--- background/storage.js | 654 +++++++++---------- background/storage_server.js | 95 +-- build.sh | 289 +++++++++ common/browser.js | 24 +- common/connection_types.js | 18 +- common/gen_unique.js | 42 +- common/lock.js | 68 +- common/once.js | 50 +- common/sha256.js | 673 ++++++++++---------- common/storage_client.js | 360 +++++------ common/stored_types.js | 46 +- common/url_item.js | 22 +- content/freezer.js | 98 +-- content/main.js | 221 ++++--- content/page_actions.js | 92 +-- copyright | 4 + html/display-panel.html | 4 +- html/display-panel.js | 14 +- html/options.html | 9 +- html/options_main.js | 1262 ++++++++++++++++++------------------- manifest.json | 52 +- 28 files changed, 2664 insertions(+), 2350 deletions(-) create mode 100755 build.sh diff --git a/README.txt b/README.txt index acff789..1342ca4 100644 --- a/README.txt +++ b/README.txt @@ -6,16 +6,17 @@ together. Such facility is necessary to enable browsing World Wide Web without executing nonfree software. Currently, the target browsers for this extension are Ungoogled Chromium -and various forks of Firefox Quantum (57+). +and various forks of Firefox (version 60+). This extension is still in an early stage. See TODOS.org. Also see `https://git.koszko.org/browser-extension-doc/' for documentation in development. ## Installation ## -The extension can be loaded into Ungoogled Chromium or a modern Gecko-based -browser as unpacked extension. As of now, project's main directory is also -the extension directory. +The extension can be "built" with `./build.sh mozilla' or `./build.sh chromium'. +This creates directories build_mozilla/ and build_chromium/, respectively. +Such directory can be loaded into Ungoogled Chromium or a modern Gecko-based +browser as unpacked extension. ## Copyright ## All copyright information is gathered in the `copyright' file which follows diff --git a/TODOS.org b/TODOS.org index 63f7985..13b9207 100644 --- a/TODOS.org +++ b/TODOS.org @@ -20,20 +20,15 @@ TODO: - make script bag components re-orderable (via drag&drop in options page) -- CRUCIAL - find some way not to require each chrome user to modify manifest.json - test with more browser forks (Abrowser, Parabola IceWeasel, LibreWolf) - - also see if browsers based on pre-quantum FF support enough of - WebExtensions for easy porting - make sure page's own csp in doesn't block our scripts -- find out how and make it possible to whitelist non-https urls and - whether we can inject csp to them - create a repository to host scripts - enable the extension to automatically fetch script substitutes from the repo - make it possible to inject scripts to arbitrary places in DOM - make script blocking code omit those scripts - check if prerendering has to be blocked -- CRUCIAL - block prefetch -- rearrange files in extension, add some mechanism to build the extension -- all solutions to modularize js code SUCK; come up with own simple DSL - to manage imports/exports +- rearrange files in extension +- supplement the build script with a makefile, also produce zipped arifacts - perform never-ending refactoring of already-written code - also implement support for whitelisting of non-https urls - validate data entered in settings @@ -49,11 +44,21 @@ TODO: (unless someone suggests another good name before we do so) - add an option to disable script blocking globally - Add support to settings_query for non-standard URLs - (e.g. file:// and about:) + (e.g. file:// and ftp://) - Process HTML files in data: URLs instead of just blocking them +- improve CSP injection for pathological cases like " + shift + done +} + +set_browser() { + if [ "x$1" = "xmozilla" -o "x$1" = "xchromium" ]; then + BROWSER="$1" + else + errcho "usage: $0 mozilla|chromium" + exit 1 + fi +} + +main() { + set_browser "$1" + + SCRIPTDIRS='background html common content' + + SCRIPTS=$(find $SCRIPTDIRS -name '*.js') + + for SCRIPT in $SCRIPTS; do + add_exports $SCRIPT + add_imports $SCRIPT + done + + eval "$(compute_scripts_list background/main.js || exit 1)" + BGSCRIPTS="$(as_json_list $COMPUTED_SCRIPTS)" + eval "$(compute_scripts_list content/main.js || exit 1)" + CONTENTSCRIPTS="$(as_json_list $COMPUTED_SCRIPTS)" + eval "$(compute_scripts_list html/display-panel.js || exit 1)" + POPUPSCRIPTS="$(as_html_list $COMPUTED_SCRIPTS)" + eval "$(compute_scripts_list html/options_main.js || exit 1)" + OPTIONSSCRIPTS="$(as_html_list $COMPUTED_SCRIPTS)" + + BUILDDIR=build_$BROWSER + rm -rf $BUILDDIR + mkdir $BUILDDIR + for DIR in $(find $SCRIPTDIRS -type d); do + mkdir -p $BUILDDIR/$DIR + done + + CHROMIUM_KEY='' + GECKO_APPLICATIONS='' + + if [ "$BROWSER" = "chromium" ]; then + CHROMIUM_KEY="\n\ +\n\ + // WARNING!!!\n\ + // EACH USER SHOULD REPLACE \"key\" WITH A UNIQUE VALUE!!!\n\ + // OTHERWISE, SECURITY CAN BE TRIVIALLY COMPROMISED!\n\ + //\n\ + // A unique key can be generated with:\n\ + // $ ssh-keygen -f /path/to/new/key.pem -t rsa -b 1024\n\ + //\n\ + // Only relevant to users of chrome-based browsers.\n\ + // Users of Firefox forks are safe.\n\ +\n\ + \"key\": \"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcnNhAAAAAwEAAQAAAIEA+0GT5WNmRRo8e5tL9+BmNtY6aBPwLIgbPnLShYBMSR40iYwLTsccrkwBXb3bs1o4p6q5WJugI8Lsia+GXZc/XHGFkq7D1aWiTxlJLs8z0JC2TQ2/yatYmBMchogYGeeUfP7aI7JJZwpATts+VhIvgga/4FYj+DijMIEpwdckqFEAAAII4Dh7HOA4exwAAAAHc3NoLXJzYQAAAIEA+0GT5WNmRRo8e5tL9+BmNtY6aBPwLIgbPnLShYBMSR40iYwLTsccrkwBXb3bs1o4p6q5WJugI8Lsia+GXZc/XHGFkq7D1aWiTxlJLs8z0JC2TQ2/yatYmBMchogYGeeUfP7aI7JJZwpATts+VhIvgga/4FYj+DijMIEpwdckqFEAAAADAQABAAAAgEHB5/MhEKMFOs8e1cMJ97ZiWubiUPlWpcqyQmauLUj1nspg3JTBh8AWJEVkaxuFgU5gYCHQmRjC6yUdywyziOEkFA4r/WpX4WmbIe+GQHRHhitLN0dgF8N6/fVNOoa5StTdfZqyl23pVXyepoDNjrJFKyupqPMmpwfH5lGr9RwBAAAAQG76HflB/5j8P2YgIYX6dQT4Ei0SqiIjNVy7jFJUQDKSJg/PYkedE02JZJBJPcMYxEJUxXtMgq+upamNILfkmY0AAABBAP4v0O5dqjy16xDDFzb4DPNAcw5Za9KJaXKVkUuKXMNZOKTR0RC/upjNTmttY980RKdIx5zA25dO8cx563bSDIsAAABBAP0MaOpBiai/eRmLqhlthHODa+Mur6W3uc9PyhWhgDBjLNMR/doaYeyfVKxtIiN3a+HkN++G+vbokRweQv++bhMAAAANdXJ6QGxvY2FsaG9zdAECAwQFBg==\"," + else + GECKO_APPLICATIONS="\n\ + \"applications\": {\n\ + \"gecko\": {\n\ + \"id\": \"{6fe13369-88e9-440f-b837-5012fb3bedec}\",\n\ + \"strict_min_version\": \"60.0\"\n\ + }\n\ + }," + fi + + sed "\ +s^_GECKO_APPLICATIONS_^$GECKO_APPLICATIONS^ +s^_CHROMIUM_KEY_^$CHROMIUM_KEY^ +s^_BGSCRIPTS_^$BGSCRIPTS^ +s^_CONTENTSCRIPTS_^$CONTENTSCRIPTS^" \ + < manifest.json > $BUILDDIR/manifest.json + + sed "s^_POPUPSCRIPTS_^$POPUPSCRIPTS^" \ + < html/display-panel.html > $BUILDDIR/html/display-panel.html + + sed "s^_OPTIONSSCRIPTS_^$OPTIONSSCRIPTS^" \ + < html/options.html > $BUILDDIR/html/options.html + + for FILE in $SCRIPTS; do + FILEKEY=$(sanitize "$FILE") + if [ "xyes" != "x$(map_get USED $FILEKEY)" ]; then + errcho "WARNING! $FILE not used" + else + (echo "\ +\"use strict\"; + +(() => { +$(map_get IMPORTCODES $FILEKEY) + +"; cat $FILE; echo " + +$(map_get EXPORTCODES $FILEKEY) +})();") > $BUILDDIR/$FILE + fi + done + + echo "\ +window.killtheweb={}; +window.browser = this.browser; /* fix for stupid Firefox */ +" > $BUILDDIR/exports_init.js + + cp -r icons/ copyright licenses/ $BUILDDIR +} + +main "$@" diff --git a/common/browser.js b/common/browser.js index 0ff3510..e50a121 100644 --- a/common/browser.js +++ b/common/browser.js @@ -5,21 +5,19 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - /* * This module normalizes access to WebExtension apis between * chrome-based and firefox-based browsers. */ -(() => { - if (typeof browser === "object") { - window.browser = browser; - window.is_chrome = false; - window.is_mozilla = true; - } else { - window.browser = window.chrome; - window.is_chrome = true; - window.is_mozilla = false; - } -})(); +const is_mozilla = typeof window.browser === "object"; +const is_chrome = !is_mozilla; +const browser = window[is_chrome ? "chrome" : "browser"]; + +/* + * EXPORTS_START + * EXPORT browser + * EXPORT is_chrome + * EXPORT is_mozilla + * EXPORTS_END + */ diff --git a/common/connection_types.js b/common/connection_types.js index cc3c976..e227532 100644 --- a/common/connection_types.js +++ b/common/connection_types.js @@ -5,18 +5,18 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - /* * Those need to be strings so they can be used as 'name' parameter * to browser.runtime.connect() */ -(() => { - const CONNECTION_TYPE = { - REMOTE_STORAGE : "0", - PAGE_ACTIONS : "1" - }; +const CONNECTION_TYPE = { + REMOTE_STORAGE : "0", + PAGE_ACTIONS : "1" +}; - window.CONNECTION_TYPE = CONNECTION_TYPE; -})(); +/* + * EXPORTS_START + * EXPORT CONNECTION_TYPE + * EXPORTS_END + */ diff --git a/common/gen_unique.js b/common/gen_unique.js index 4bcfd67..7ec8b4a 100644 --- a/common/gen_unique.js +++ b/common/gen_unique.js @@ -5,25 +5,29 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - -(() => { - const sha256 = window.sha256; - const browser = window.browser; - const is_chrome = window.is_chrome; +/* + * IMPORTS_START + * IMPORT sha256 + * IMPORT browser + * IMPORT is_chrome + * IMPORTS_END + */ - function get_id() - { - if (is_chrome) - return browser.runtime.getManifest().key.substring(0, 50); - else - return browser.runtime.getURL("dummy"); - } +function get_id() +{ + if (is_chrome) + return browser.runtime.getManifest().key.substring(0, 50); + else + return browser.runtime.getURL("dummy"); +} - function gen_unique(url) - { - return "#" + sha256(get_id() + url); - } +function gen_unique(url) +{ + return "#" + sha256(get_id() + url); +} - window.gen_unique = gen_unique; -})(); +/* + * EXPORTS_START + * EXPORT gen_unique + * EXPORTS_END + */ diff --git a/common/lock.js b/common/lock.js index 3358393..1130762 100644 --- a/common/lock.js +++ b/common/lock.js @@ -19,40 +19,40 @@ * in a promise. */ -"use strict"; - -(() => { - function make_lock() { - return {free: true, queue: []}; +function make_lock() { + return {free: true, queue: []}; +} + +function _lock(lock, cb) { + if (lock.free) { + lock.free = false; + setTimeout(cb); + } else { + lock.queue.push(cb); } - - function _lock(lock, cb) { - if (lock.free) { - lock.free = false; - setTimeout(cb); - } else { - lock.queue.push(cb); - } +} + +function lock(lock) { + return new Promise((resolve, reject) => _lock(lock, resolve)); +} + +function unlock(lock) { + if (lock.free) + throw new Exception("Attempting to release a free lock"); + + if (lock.queue.length === 0) { + lock.free = true; + } else { + let cb = lock.queue[0]; + lock.queue.splice(0, 1); + setTimeout(cb); } +} - function lock(lock) { - return new Promise((resolve, reject) => _lock(lock, resolve)); - } - - function unlock(lock) { - if (lock.free) - throw new Exception("Attempting to release a free lock"); - - if (lock.queue.length === 0) { - lock.free = true; - } else { - let cb = lock.queue[0]; - lock.queue.splice(0, 1); - setTimeout(cb); - } - } - - window.make_lock = make_lock; - window.lock = lock; - window.unlock = unlock; -})(); +/* + * EXPORTS_START + * EXPORT make_lock + * EXPORT lock + * EXPORT unlock + * EXPORTS_END + */ diff --git a/common/once.js b/common/once.js index d56b3c8..e3e6dbe 100644 --- a/common/once.js +++ b/common/once.js @@ -5,37 +5,37 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - /* * This module provides an easy way to wrap an async function into a promise * so that it only gets executed once. */ -(() => { - async function assign_result(state, result_producer) - { - state.result = await result_producer(); - state.ready = true; - for (let cb of state.waiting) - setTimeout(cb, 0, state.result); - state.waiting = undefined; - } +async function assign_result(state, result_producer) +{ + state.result = await result_producer(); + state.ready = true; + for (let cb of state.waiting) + setTimeout(cb, 0, state.result); + state.waiting = undefined; +} - async function get_result(state) - { - if (state.ready) - return state.result; +async function get_result(state) +{ + if (state.ready) + return state.result; - return new Promise((resolve, reject) => state.waiting.push(resolve)); - } + return new Promise((resolve, reject) => state.waiting.push(resolve)); +} - function make_once(result_producer) - { - let state = {waiting : [], ready : false, result : undefined}; - assign_result(state, result_producer); - return () => get_result(state); - } +function make_once(result_producer) +{ + let state = {waiting : [], ready : false, result : undefined}; + assign_result(state, result_producer); + return () => get_result(state); +} - window.make_once = make_once; -})(); +/* + * EXPORTS_START + * EXPORT make_once + * EXPORTS_END + */ diff --git a/common/sha256.js b/common/sha256.js index 271ec87..4fa07b5 100644 --- a/common/sha256.js +++ b/common/sha256.js @@ -7,32 +7,28 @@ * @license MIT */ -"use strict"; +var fake_window = {}; -(() => { - var fake_window = {}; - - console.log('hello, crypto!'); - var ERROR = 'input is invalid type'; - var WINDOW = typeof window === 'object'; - var root = /*WINDOW ? window : {}*/ fake_window; - if (root.JS_SHA256_NO_WINDOW) { +var ERROR = 'input is invalid type'; +var WINDOW = typeof fake_window === 'object'; +var root = /*WINDOW ? window : {}*/ fake_window; +if (root.JS_SHA256_NO_WINDOW) { WINDOW = false; - } - var WEB_WORKER = !WINDOW && typeof self === 'object'; - var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; - if (NODE_JS) { +} +var WEB_WORKER = !WINDOW && typeof self === 'object'; +var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; +if (NODE_JS) { root = global; - } else if (WEB_WORKER) { +} else if (WEB_WORKER) { root = self; - } - var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports; - var AMD = typeof define === 'function' && define.amd; - var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; - var HEX_CHARS = '0123456789abcdef'.split(''); - var EXTRA = [-2147483648, 8388608, 32768, 128]; - var SHIFT = [24, 16, 8, 0]; - var K = [ +} +var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports; +var AMD = typeof define === 'function' && define.amd; +var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; +var HEX_CHARS = '0123456789abcdef'.split(''); +var EXTRA = [-2147483648, 8388608, 32768, 128]; +var SHIFT = [24, 16, 8, 0]; +var K = [ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, @@ -41,209 +37,209 @@ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - ]; - var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; +]; +var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; - var blocks = []; +var blocks = []; - if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { +if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { Array.isArray = function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; + return Object.prototype.toString.call(obj) === '[object Array]'; }; - } +} - if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { +if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { ArrayBuffer.isView = function (obj) { - return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; + return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; }; - } +} - var createOutputMethod = function (outputType, is224) { +var createOutputMethod = function (outputType, is224) { return function (message) { - return new Sha256(is224, true).update(message)[outputType](); + return new Sha256(is224, true).update(message)[outputType](); }; - }; +}; - var createMethod = function (is224) { +var createMethod = function (is224) { var method = createOutputMethod('hex', is224); if (NODE_JS) { - method = nodeWrap(method, is224); + method = nodeWrap(method, is224); } method.create = function () { - return new Sha256(is224); + return new Sha256(is224); }; method.update = function (message) { - return method.create().update(message); + return method.create().update(message); }; for (var i = 0; i < OUTPUT_TYPES.length; ++i) { - var type = OUTPUT_TYPES[i]; - method[type] = createOutputMethod(type, is224); + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type, is224); } return method; - }; +}; - var nodeWrap = function (method, is224) { +var nodeWrap = function (method, is224) { var crypto = eval("require('crypto')"); var Buffer = eval("require('buffer').Buffer"); var algorithm = is224 ? 'sha224' : 'sha256'; var nodeMethod = function (message) { - if (typeof message === 'string') { - return crypto.createHash(algorithm).update(message, 'utf8').digest('hex'); - } else { - if (message === null || message === undefined) { - throw new Error(ERROR); - } else if (message.constructor === ArrayBuffer) { - message = new Uint8Array(message); - } - } - if (Array.isArray(message) || ArrayBuffer.isView(message) || - message.constructor === Buffer) { - return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex'); - } else { - return method(message); - } + if (typeof message === 'string') { + return crypto.createHash(algorithm).update(message, 'utf8').digest('hex'); + } else { + if (message === null || message === undefined) { + throw new Error(ERROR); + } else if (message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } + } + if (Array.isArray(message) || ArrayBuffer.isView(message) || + message.constructor === Buffer) { + return crypto.createHash(algorithm).update(new Buffer(message)).digest('hex'); + } else { + return method(message); + } }; return nodeMethod; - }; +}; - var createHmacOutputMethod = function (outputType, is224) { +var createHmacOutputMethod = function (outputType, is224) { return function (key, message) { - return new HmacSha256(key, is224, true).update(message)[outputType](); + return new HmacSha256(key, is224, true).update(message)[outputType](); }; - }; +}; - var createHmacMethod = function (is224) { +var createHmacMethod = function (is224) { var method = createHmacOutputMethod('hex', is224); method.create = function (key) { - return new HmacSha256(key, is224); + return new HmacSha256(key, is224); }; method.update = function (key, message) { - return method.create(key).update(message); + return method.create(key).update(message); }; for (var i = 0; i < OUTPUT_TYPES.length; ++i) { - var type = OUTPUT_TYPES[i]; - method[type] = createHmacOutputMethod(type, is224); + var type = OUTPUT_TYPES[i]; + method[type] = createHmacOutputMethod(type, is224); } return method; - }; +}; - function Sha256(is224, sharedMemory) { +function Sha256(is224, sharedMemory) { if (sharedMemory) { - blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - this.blocks = blocks; + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; } else { - this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; } if (is224) { - this.h0 = 0xc1059ed8; - this.h1 = 0x367cd507; - this.h2 = 0x3070dd17; - this.h3 = 0xf70e5939; - this.h4 = 0xffc00b31; - this.h5 = 0x68581511; - this.h6 = 0x64f98fa7; - this.h7 = 0xbefa4fa4; + this.h0 = 0xc1059ed8; + this.h1 = 0x367cd507; + this.h2 = 0x3070dd17; + this.h3 = 0xf70e5939; + this.h4 = 0xffc00b31; + this.h5 = 0x68581511; + this.h6 = 0x64f98fa7; + this.h7 = 0xbefa4fa4; } else { // 256 - this.h0 = 0x6a09e667; - this.h1 = 0xbb67ae85; - this.h2 = 0x3c6ef372; - this.h3 = 0xa54ff53a; - this.h4 = 0x510e527f; - this.h5 = 0x9b05688c; - this.h6 = 0x1f83d9ab; - this.h7 = 0x5be0cd19; + this.h0 = 0x6a09e667; + this.h1 = 0xbb67ae85; + this.h2 = 0x3c6ef372; + this.h3 = 0xa54ff53a; + this.h4 = 0x510e527f; + this.h5 = 0x9b05688c; + this.h6 = 0x1f83d9ab; + this.h7 = 0x5be0cd19; } this.block = this.start = this.bytes = this.hBytes = 0; this.finalized = this.hashed = false; this.first = true; this.is224 = is224; - } +} - Sha256.prototype.update = function (message) { +Sha256.prototype.update = function (message) { if (this.finalized) { - return; + return; } var notString, type = typeof message; if (type !== 'string') { - if (type === 'object') { - if (message === null) { - throw new Error(ERROR); - } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { - message = new Uint8Array(message); - } else if (!Array.isArray(message)) { - if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { + if (type === 'object') { + if (message === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } else if (!Array.isArray(message)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { + throw new Error(ERROR); + } + } + } else { throw new Error(ERROR); - } - } - } else { - throw new Error(ERROR); - } - notString = true; + } + notString = true; } var code, index = 0, i, length = message.length, blocks = this.blocks; while (index < length) { - if (this.hashed) { - this.hashed = false; - blocks[0] = this.block; - blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - - if (notString) { - for (i = this.start; index < length && i < 64; ++index) { - blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; - } - } else { - for (i = this.start; index < length && i < 64; ++index) { - code = message.charCodeAt(index); - if (code < 0x80) { - blocks[i >> 2] |= code << SHIFT[i++ & 3]; - } else if (code < 0x800) { - blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else { - code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); - blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } - - this.lastByteIndex = i; - this.bytes += i - this.start; - if (i >= 64) { - this.block = blocks[16]; - this.start = i - 64; - this.hash(); - this.hashed = true; - } else { - this.start = i; - } + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + + if (notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >> 2] |= message[index] << SHIFT[i++ & 3]; + } + } else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >> 2] |= code << SHIFT[i++ & 3]; + } else if (code < 0x800) { + blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } + } + + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } else { + this.start = i; + } } if (this.bytes > 4294967295) { - this.hBytes += this.bytes / 4294967296 << 0; - this.bytes = this.bytes % 4294967296; + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; } return this; - }; +}; - Sha256.prototype.finalize = function () { +Sha256.prototype.finalize = function () { if (this.finalized) { - return; + return; } this.finalized = true; var blocks = this.blocks, i = this.lastByteIndex; @@ -251,86 +247,86 @@ blocks[i >> 2] |= EXTRA[i & 3]; this.block = blocks[16]; if (i >= 56) { - if (!this.hashed) { - this.hash(); - } - blocks[0] = this.block; - blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; } blocks[14] = this.hBytes << 3 | this.bytes >>> 29; blocks[15] = this.bytes << 3; this.hash(); - }; +}; - Sha256.prototype.hash = function () { +Sha256.prototype.hash = function () { var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, - h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc; + h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc; for (j = 16; j < 64; ++j) { - // rightrotate - t1 = blocks[j - 15]; - s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); - t1 = blocks[j - 2]; - s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10); - blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0; + // rightrotate + t1 = blocks[j - 15]; + s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); + t1 = blocks[j - 2]; + s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10); + blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0; } bc = b & c; for (j = 0; j < 64; j += 4) { - if (this.first) { - if (this.is224) { - ab = 300032; - t1 = blocks[0] - 1413257819; - h = t1 - 150054599 << 0; - d = t1 + 24177077 << 0; - } else { - ab = 704751109; - t1 = blocks[0] - 210244248; - h = t1 - 1521486534 << 0; - d = t1 + 143694565 << 0; - } - this.first = false; - } else { - s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); - s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); - ab = a & b; - maj = ab ^ (a & c) ^ bc; - ch = (e & f) ^ (~e & g); - t1 = h + s1 + ch + K[j] + blocks[j]; - t2 = s0 + maj; - h = d + t1 << 0; - d = t1 + t2 << 0; - } - s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10)); - s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7)); - da = d & a; - maj = da ^ (d & b) ^ ab; - ch = (h & e) ^ (~h & f); - t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; - t2 = s0 + maj; - g = c + t1 << 0; - c = t1 + t2 << 0; - s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10)); - s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7)); - cd = c & d; - maj = cd ^ (c & a) ^ da; - ch = (g & h) ^ (~g & e); - t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; - t2 = s0 + maj; - f = b + t1 << 0; - b = t1 + t2 << 0; - s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10)); - s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7)); - bc = b & c; - maj = bc ^ (b & d) ^ cd; - ch = (f & g) ^ (~f & h); - t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; - t2 = s0 + maj; - e = a + t1 << 0; - a = t1 + t2 << 0; + if (this.first) { + if (this.is224) { + ab = 300032; + t1 = blocks[0] - 1413257819; + h = t1 - 150054599 << 0; + d = t1 + 24177077 << 0; + } else { + ab = 704751109; + t1 = blocks[0] - 210244248; + h = t1 - 1521486534 << 0; + d = t1 + 143694565 << 0; + } + this.first = false; + } else { + s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); + s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); + ab = a & b; + maj = ab ^ (a & c) ^ bc; + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + K[j] + blocks[j]; + t2 = s0 + maj; + h = d + t1 << 0; + d = t1 + t2 << 0; + } + s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10)); + s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7)); + da = d & a; + maj = da ^ (d & b) ^ ab; + ch = (h & e) ^ (~h & f); + t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; + t2 = s0 + maj; + g = c + t1 << 0; + c = t1 + t2 << 0; + s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10)); + s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7)); + cd = c & d; + maj = cd ^ (c & a) ^ da; + ch = (g & h) ^ (~g & e); + t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; + t2 = s0 + maj; + f = b + t1 << 0; + b = t1 + t2 << 0; + s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10)); + s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7)); + bc = b & c; + maj = bc ^ (b & d) ^ cd; + ch = (f & g) ^ (~f & h); + t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; + t2 = s0 + maj; + e = a + t1 << 0; + a = t1 + t2 << 0; } this.h0 = this.h0 + a << 0; @@ -341,77 +337,77 @@ this.h5 = this.h5 + f << 0; this.h6 = this.h6 + g << 0; this.h7 = this.h7 + h << 0; - }; +}; - Sha256.prototype.hex = function () { +Sha256.prototype.hex = function () { this.finalize(); var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, - h6 = this.h6, h7 = this.h7; + h6 = this.h6, h7 = this.h7; var hex = HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] + - HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + - HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + - HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + - HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + - HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + - HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + - HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + - HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + - HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + - HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + - HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + - HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + - HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + - HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + - HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + - HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + - HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + - HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + - HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + - HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] + - HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] + - HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] + - HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + - HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] + - HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] + - HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] + - HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F]; + HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] + + HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] + + HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + + HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] + + HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] + + HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] + + HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + + HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] + + HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] + + HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] + + HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + + HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F] + + HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] + + HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] + + HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + + HEX_CHARS[(h4 >> 28) & 0x0F] + HEX_CHARS[(h4 >> 24) & 0x0F] + + HEX_CHARS[(h4 >> 20) & 0x0F] + HEX_CHARS[(h4 >> 16) & 0x0F] + + HEX_CHARS[(h4 >> 12) & 0x0F] + HEX_CHARS[(h4 >> 8) & 0x0F] + + HEX_CHARS[(h4 >> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + + HEX_CHARS[(h5 >> 28) & 0x0F] + HEX_CHARS[(h5 >> 24) & 0x0F] + + HEX_CHARS[(h5 >> 20) & 0x0F] + HEX_CHARS[(h5 >> 16) & 0x0F] + + HEX_CHARS[(h5 >> 12) & 0x0F] + HEX_CHARS[(h5 >> 8) & 0x0F] + + HEX_CHARS[(h5 >> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + + HEX_CHARS[(h6 >> 28) & 0x0F] + HEX_CHARS[(h6 >> 24) & 0x0F] + + HEX_CHARS[(h6 >> 20) & 0x0F] + HEX_CHARS[(h6 >> 16) & 0x0F] + + HEX_CHARS[(h6 >> 12) & 0x0F] + HEX_CHARS[(h6 >> 8) & 0x0F] + + HEX_CHARS[(h6 >> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F]; if (!this.is224) { - hex += HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] + - HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] + - HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] + - HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]; + hex += HEX_CHARS[(h7 >> 28) & 0x0F] + HEX_CHARS[(h7 >> 24) & 0x0F] + + HEX_CHARS[(h7 >> 20) & 0x0F] + HEX_CHARS[(h7 >> 16) & 0x0F] + + HEX_CHARS[(h7 >> 12) & 0x0F] + HEX_CHARS[(h7 >> 8) & 0x0F] + + HEX_CHARS[(h7 >> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]; } return hex; - }; +}; - Sha256.prototype.toString = Sha256.prototype.hex; +Sha256.prototype.toString = Sha256.prototype.hex; - Sha256.prototype.digest = function () { +Sha256.prototype.digest = function () { this.finalize(); var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, - h6 = this.h6, h7 = this.h7; + h6 = this.h6, h7 = this.h7; var arr = [ - (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, - (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, - (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, - (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, - (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF, - (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, h5 & 0xFF, - (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, h6 & 0xFF + (h0 >> 24) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 8) & 0xFF, h0 & 0xFF, + (h1 >> 24) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 8) & 0xFF, h1 & 0xFF, + (h2 >> 24) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 8) & 0xFF, h2 & 0xFF, + (h3 >> 24) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 8) & 0xFF, h3 & 0xFF, + (h4 >> 24) & 0xFF, (h4 >> 16) & 0xFF, (h4 >> 8) & 0xFF, h4 & 0xFF, + (h5 >> 24) & 0xFF, (h5 >> 16) & 0xFF, (h5 >> 8) & 0xFF, h5 & 0xFF, + (h6 >> 24) & 0xFF, (h6 >> 16) & 0xFF, (h6 >> 8) & 0xFF, h6 & 0xFF ]; if (!this.is224) { - arr.push((h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, h7 & 0xFF); + arr.push((h7 >> 24) & 0xFF, (h7 >> 16) & 0xFF, (h7 >> 8) & 0xFF, h7 & 0xFF); } return arr; - }; +}; - Sha256.prototype.array = Sha256.prototype.digest; +Sha256.prototype.array = Sha256.prototype.digest; - Sha256.prototype.arrayBuffer = function () { +Sha256.prototype.arrayBuffer = function () { this.finalize(); var buffer = new ArrayBuffer(this.is224 ? 28 : 32); @@ -424,60 +420,60 @@ dataView.setUint32(20, this.h5); dataView.setUint32(24, this.h6); if (!this.is224) { - dataView.setUint32(28, this.h7); + dataView.setUint32(28, this.h7); } return buffer; - }; +}; - function HmacSha256(key, is224, sharedMemory) { +function HmacSha256(key, is224, sharedMemory) { var i, type = typeof key; if (type === 'string') { - var bytes = [], length = key.length, index = 0, code; - for (i = 0; i < length; ++i) { - code = key.charCodeAt(i); - if (code < 0x80) { - bytes[index++] = code; - } else if (code < 0x800) { - bytes[index++] = (0xc0 | (code >> 6)); - bytes[index++] = (0x80 | (code & 0x3f)); - } else if (code < 0xd800 || code >= 0xe000) { - bytes[index++] = (0xe0 | (code >> 12)); - bytes[index++] = (0x80 | ((code >> 6) & 0x3f)); - bytes[index++] = (0x80 | (code & 0x3f)); - } else { - code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); - bytes[index++] = (0xf0 | (code >> 18)); - bytes[index++] = (0x80 | ((code >> 12) & 0x3f)); - bytes[index++] = (0x80 | ((code >> 6) & 0x3f)); - bytes[index++] = (0x80 | (code & 0x3f)); - } - } - key = bytes; + var bytes = [], length = key.length, index = 0, code; + for (i = 0; i < length; ++i) { + code = key.charCodeAt(i); + if (code < 0x80) { + bytes[index++] = code; + } else if (code < 0x800) { + bytes[index++] = (0xc0 | (code >> 6)); + bytes[index++] = (0x80 | (code & 0x3f)); + } else if (code < 0xd800 || code >= 0xe000) { + bytes[index++] = (0xe0 | (code >> 12)); + bytes[index++] = (0x80 | ((code >> 6) & 0x3f)); + bytes[index++] = (0x80 | (code & 0x3f)); + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); + bytes[index++] = (0xf0 | (code >> 18)); + bytes[index++] = (0x80 | ((code >> 12) & 0x3f)); + bytes[index++] = (0x80 | ((code >> 6) & 0x3f)); + bytes[index++] = (0x80 | (code & 0x3f)); + } + } + key = bytes; } else { - if (type === 'object') { - if (key === null) { - throw new Error(ERROR); - } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { - key = new Uint8Array(key); - } else if (!Array.isArray(key)) { - if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { + if (type === 'object') { + if (key === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { + key = new Uint8Array(key); + } else if (!Array.isArray(key)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { + throw new Error(ERROR); + } + } + } else { throw new Error(ERROR); - } - } - } else { - throw new Error(ERROR); - } + } } if (key.length > 64) { - key = (new Sha256(is224, true)).update(key).array(); + key = (new Sha256(is224, true)).update(key).array(); } var oKeyPad = [], iKeyPad = []; for (i = 0; i < 64; ++i) { - var b = key[i] || 0; - oKeyPad[i] = 0x5c ^ b; - iKeyPad[i] = 0x36 ^ b; + var b = key[i] || 0; + oKeyPad[i] = 0x5c ^ b; + iKeyPad[i] = 0x36 ^ b; } Sha256.call(this, is224, sharedMemory); @@ -486,38 +482,43 @@ this.oKeyPad = oKeyPad; this.inner = true; this.sharedMemory = sharedMemory; - } - HmacSha256.prototype = new Sha256(); +} +HmacSha256.prototype = new Sha256(); - HmacSha256.prototype.finalize = function () { +HmacSha256.prototype.finalize = function () { Sha256.prototype.finalize.call(this); if (this.inner) { - this.inner = false; - var innerHash = this.array(); - Sha256.call(this, this.is224, this.sharedMemory); - this.update(this.oKeyPad); - this.update(innerHash); - Sha256.prototype.finalize.call(this); + this.inner = false; + var innerHash = this.array(); + Sha256.call(this, this.is224, this.sharedMemory); + this.update(this.oKeyPad); + this.update(innerHash); + Sha256.prototype.finalize.call(this); } - }; +}; - var exports = createMethod(); - exports.sha256 = exports; - exports.sha224 = createMethod(true); - exports.sha256.hmac = createHmacMethod(); - exports.sha224.hmac = createHmacMethod(true); +var exports = createMethod(); +exports.sha256 = exports; +exports.sha224 = createMethod(true); +exports.sha256.hmac = createHmacMethod(); +exports.sha224.hmac = createHmacMethod(true); - if (COMMON_JS) { +if (COMMON_JS) { module.exports = exports; - } else { +} else { root.sha256 = exports.sha256; root.sha224 = exports.sha224; if (AMD) { - define(function () { - return exports; - }); + define(function () { + return exports; + }); } - } +} + +const sha256 = fake_window.sha256; - window.sha256 = fake_window.sha256; -})(); +/* + * EXPORTS_START + * EXPORT sha256 + * EXPORTS_END + */ diff --git a/common/storage_client.js b/common/storage_client.js index 1bc9a88..2bbddb6 100644 --- a/common/storage_client.js +++ b/common/storage_client.js @@ -5,182 +5,188 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - -(() => { - const CONNECTION_TYPE = window.CONNECTION_TYPE; - const TYPE_PREFIX = window.TYPE_PREFIX; - const list_prefixes = window.list_prefixes; - const make_once = window.make_once; - const browser = window.browser; - - var call_id = 0; - var port; - var calls_waiting = new Map(); - - function set_call_callback(resolve, reject, func, args) - { - port.postMessage([call_id, func, args]); - calls_waiting.set(call_id++, [resolve, reject]); - } - - async function remote_call(func, args) - { - return new Promise((resolve, reject) => - set_call_callback(resolve, reject, func, args)); - } - - function handle_message(message) - { - let callbacks = calls_waiting.get(message.call_id); - if (callbacks === undefined) { - handle_change(message); - return; - } - - let [resolve, reject] = callbacks; - calls_waiting.delete(message.call_id); - if (message.error !== undefined) - setTimeout(reject, 0, message.error); - else - setTimeout(resolve, 0, message.result); - } - - function list(name, prefix) - { - return {prefix, name, listeners : new Set()}; - } - - var scripts = list("scripts", TYPE_PREFIX.SCRIPT); - var bags = list("bags", TYPE_PREFIX.BAG); - var pages = list("pages", TYPE_PREFIX.PAGE); - - const list_by_prefix = { - [TYPE_PREFIX.SCRIPT] : scripts, - [TYPE_PREFIX.BAG] : bags, - [TYPE_PREFIX.PAGE] : pages - }; - - var resolve_init; - - function handle_first_message(message) - { - for (let prefix of Object.keys(message)) - list_by_prefix[prefix].map = new Map(message[prefix]); - - port.onMessage.removeListener(handle_first_message); - port.onMessage.addListener(handle_message); - - resolve_init(); - } - - function handle_change(change) - { - let list = list_by_prefix[change.prefix]; - - if (change.new_val === undefined) - list.map.delete(change.item); - else - list.map.set(change.item, change.new_val); - - for (let listener_callback of list.listeners) - listener_callback(change); - } - - var exports = {}; - - function start_connection(resolve) - { - resolve_init = resolve; - port = browser.runtime.connect({name : CONNECTION_TYPE.REMOTE_STORAGE}); - port.onMessage.addListener(handle_first_message); - } - - async function init() { - await new Promise((resolve, reject) => start_connection(resolve)); - return exports; - } - - for (let call_name of ["set", "remove", "replace", "clear"]) - exports [call_name] = (...args) => remote_call(call_name, args); - - // TODO: Much of the code below is copy-pasted from /background/storage.mjs. - // This should later be refactored into a separate module - // to avoid duplication. - - /* - * Facilitate listening to changes - */ - - exports.add_change_listener = function (cb, prefixes=list_prefixes) - { - if (typeof(prefixes) === "string") - prefixes = [prefixes]; - - for (let prefix of prefixes) - list_by_prefix[prefix].listeners.add(cb); - } - - exports.remove_change_listener = function (cb, prefixes=list_prefixes) - { - if (typeof(prefixes) === "string") - prefixes = [prefixes]; - - for (let prefix of prefixes) - list_by_prefix[prefix].listeners.delete(cb); - } - - /* Prepare some hepler functions to get elements of a list */ - - function list_items_it(list, with_values=false) - { - return with_values ? list.map.entries() : list.map.keys(); - } - - function list_entries_it(list) - { - return list_items_it(list, true); - } - - function list_items(list, with_values=false) - { - let array = []; - - for (let item of list_items_it(list, with_values)) - array.push(item); - - return array; - } - - function list_entries(list) - { - return list_items(list, true); - } - - exports.get = function (prefix, item) - { - return list_by_prefix[prefix].map.get(item); - } - - exports.get_all_names = function (prefix) - { - return list_items(list_by_prefix[prefix]); - } - - exports.get_all_names_it = function (prefix) - { - return list_items_it(list_by_prefix[prefix]); - } - - exports.get_all = function (prefix) - { - return list_entries(list_by_prefix[prefix]); - } +/* + * IMPORTS_START + * IMPORT CONNECTION_TYPE + * IMPORT TYPE_PREFIX + * IMPORT list_prefixes + * IMPORT make_once + * IMPORT browser + * IMPORTS_END + */ - exports.get_all_it = function (prefix) - { - return list_entries_it(list_by_prefix[prefix]); - } +var call_id = 0; +var port; +var calls_waiting = new Map(); + +function set_call_callback(resolve, reject, func, args) +{ + port.postMessage([call_id, func, args]); + calls_waiting.set(call_id++, [resolve, reject]); +} + +async function remote_call(func, args) +{ + return new Promise((resolve, reject) => + set_call_callback(resolve, reject, func, args)); +} + +function handle_message(message) +{ + let callbacks = calls_waiting.get(message.call_id); + if (callbacks === undefined) { + handle_change(message); + return; + } + + let [resolve, reject] = callbacks; + calls_waiting.delete(message.call_id); + if (message.error !== undefined) + setTimeout(reject, 0, message.error); + else + setTimeout(resolve, 0, message.result); +} + +function list(name, prefix) +{ + return {prefix, name, listeners : new Set()}; +} + +var scripts = list("scripts", TYPE_PREFIX.SCRIPT); +var bags = list("bags", TYPE_PREFIX.BAG); +var pages = list("pages", TYPE_PREFIX.PAGE); + +const list_by_prefix = { + [TYPE_PREFIX.SCRIPT] : scripts, + [TYPE_PREFIX.BAG] : bags, + [TYPE_PREFIX.PAGE] : pages +}; + +var resolve_init; + +function handle_first_message(message) +{ + for (let prefix of Object.keys(message)) + list_by_prefix[prefix].map = new Map(message[prefix]); + + port.onMessage.removeListener(handle_first_message); + port.onMessage.addListener(handle_message); + + resolve_init(); +} + +function handle_change(change) +{ + let list = list_by_prefix[change.prefix]; + + if (change.new_val === undefined) + list.map.delete(change.item); + else + list.map.set(change.item, change.new_val); + + for (let listener_callback of list.listeners) + listener_callback(change); +} + +var exports = {}; + +function start_connection(resolve) +{ + resolve_init = resolve; + port = browser.runtime.connect({name : CONNECTION_TYPE.REMOTE_STORAGE}); + port.onMessage.addListener(handle_first_message); +} + +async function init() { + await new Promise((resolve, reject) => start_connection(resolve)); + return exports; +} + +for (let call_name of ["set", "remove", "replace", "clear"]) + exports [call_name] = (...args) => remote_call(call_name, args); + +// TODO: Much of the code below is copy-pasted from /background/storage.mjs. +// This should later be refactored into a separate module +// to avoid duplication. + +/* + * Facilitate listening to changes + */ - window.get_storage = make_once(init); -})(); +exports.add_change_listener = function (cb, prefixes=list_prefixes) +{ + if (typeof(prefixes) === "string") + prefixes = [prefixes]; + + for (let prefix of prefixes) + list_by_prefix[prefix].listeners.add(cb); +} + +exports.remove_change_listener = function (cb, prefixes=list_prefixes) +{ + if (typeof(prefixes) === "string") + prefixes = [prefixes]; + + for (let prefix of prefixes) + list_by_prefix[prefix].listeners.delete(cb); +} + +/* Prepare some hepler functions to get elements of a list */ + +function list_items_it(list, with_values=false) +{ + return with_values ? list.map.entries() : list.map.keys(); +} + +function list_entries_it(list) +{ + return list_items_it(list, true); +} + +function list_items(list, with_values=false) +{ + let array = []; + + for (let item of list_items_it(list, with_values)) + array.push(item); + + return array; +} + +function list_entries(list) +{ + return list_items(list, true); +} + +exports.get = function (prefix, item) +{ + return list_by_prefix[prefix].map.get(item); +} + +exports.get_all_names = function (prefix) +{ + return list_items(list_by_prefix[prefix]); +} + +exports.get_all_names_it = function (prefix) +{ + return list_items_it(list_by_prefix[prefix]); +} + +exports.get_all = function (prefix) +{ + return list_entries(list_by_prefix[prefix]); +} + +exports.get_all_it = function (prefix) +{ + return list_entries_it(list_by_prefix[prefix]); +} + +const get_remote_storage = make_once(init); + +/* + * EXPORTS_START + * EXPORT get_remote_storage + * EXPORTS_END + */ diff --git a/common/stored_types.js b/common/stored_types.js index e043777..ef1339f 100644 --- a/common/stored_types.js +++ b/common/stored_types.js @@ -13,29 +13,29 @@ * an underscore. */ -"use strict"; +const TYPE_PREFIX = { + PAGE : "p", + BAG : "b", + SCRIPT : "s", + VAR : "_" +}; -(() => { - const TYPE_PREFIX = { - PAGE : "p", - BAG : "b", - SCRIPT : "s", - VAR : "_" - }; +const TYPE_NAME = { + [TYPE_PREFIX.PAGE] : "page", + [TYPE_PREFIX.BAG] : "bag", + [TYPE_PREFIX.SCRIPT] : "script" +} - const TYPE_NAME = { - [TYPE_PREFIX.PAGE] : "page", - [TYPE_PREFIX.BAG] : "bag", - [TYPE_PREFIX.SCRIPT] : "script" - } +const list_prefixes = [ + TYPE_PREFIX.PAGE, + TYPE_PREFIX.BAG, + TYPE_PREFIX.SCRIPT +]; - const list_prefixes = [ - TYPE_PREFIX.PAGE, - TYPE_PREFIX.BAG, - TYPE_PREFIX.SCRIPT - ]; - - window.TYPE_PREFIX = TYPE_PREFIX; - window.TYPE_NAME = TYPE_NAME; - window.list_prefixes = list_prefixes; -})(); +/* + * EXPORTS_START + * EXPORT TYPE_PREFIX + * EXPORT TYPE_NAME + * EXPORT list_prefixes + * EXPORTS_END + */ diff --git a/common/url_item.js b/common/url_item.js index 9f4b118..102f117 100644 --- a/common/url_item.js +++ b/common/url_item.js @@ -5,15 +5,15 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; +function url_item(url) +{ + let url_re = /^([^?#]*).*$/; + let match = url_re.exec(url); + return match[1]; +} -(() => { - function url_item(url) - { - let url_re = /^([^?#]*).*$/; - let match = url_re.exec(url); - return match[1]; - } - - window.url_item = url_item; -})(); +/* + * EXPORTS_START + * EXPORT url_item + * EXPORTS_END + */ diff --git a/content/freezer.js b/content/freezer.js index cdd0709..1696f53 100644 --- a/content/freezer.js +++ b/content/freezer.js @@ -6,58 +6,60 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; +const loaderAttributes = ["href", "src", "data"]; +const jsOrDataUrlRx = /^(?:data:(?:[^,;]*ml|unknown-content-type)|javascript:)/i; -(() => { - const loaderAttributes = ["href", "src", "data"]; - const jsOrDataUrlRx = /^(?:data:(?:[^,;]*ml|unknown-content-type)|javascript:)/i; +function sanitize_attributes(element) { + if (element._frozen) + return; + let fa = []; + let loaders = []; + let attributes = element.attributes || []; - function sanitizeAttributes(element) { - if (element._frozen) - return; - let fa = []; - let loaders = []; - for (let a of element.attributes) { - let name = a.localName.toLowerCase(); - if (loaderAttributes.includes(name)) - if (jsOrDataUrlRx.test(a.value)) - loaders.push(a); + for (let a of attributes) { + let name = a.localName.toLowerCase(); + if (loaderAttributes.includes(name)) + if (jsOrDataUrlRx.test(a.value)) + loaders.push(a); - else if (name.startsWith("on")) { - console.debug("Removing", a, element.outerHTML); - fa.push(a.cloneNode()); - a.value = ""; - element[name] = null; - } + else if (name.startsWith("on")) { + console.debug("Removing", a, element.outerHTML); + fa.push(a.cloneNode()); + a.value = ""; + element[name] = null; } - if (loaders.length) { - for (let a of loaders) { - fa.push(a.cloneNode()); - a.value = "javascript://frozen"; - } - if ("contentWindow" in element) - element.replaceWith(element = element.cloneNode(true)); - + } + if (loaders.length) { + for (let a of loaders) { + fa.push(a.cloneNode()); + a.value = "javascript://frozen"; } - if (fa.length) - element._frozenAttributes = fa; - element._frozen = true; + if ("contentWindow" in element) + element.replaceWith(element = element.cloneNode(true)); + } - - function scriptSuppressor(nonce) { - const blockExecute = e => { - if (document.readyState === 'complete') { - removeEventListener('beforescriptexecute', blockExecute, true); - return; - } - else if (e.isTrusted && e.target.getAttribute('nonce') !== nonce) { // Prevent blocking of injected scripts - e.preventDefault(); - console.log('Suppressed script', e.target); - } - }; - return blockExecute; + if (fa.length) + element._frozenAttributes = fa; + element._frozen = true; +} + +function script_suppressor(nonce) { + const blockExecute = e => { + if (document.readyState === 'complete') { + removeEventListener('beforescriptexecute', blockExecute, true); + return; + } + else if (e.isTrusted && e.target.getAttribute('nonce') !== nonce) { // Prevent blocking of injected scripts + e.preventDefault(); + console.log('Suppressed script', e.target); + } }; - - window.scriptSuppressor = scriptSuppressor; - window.sanitize_attributes = sanitizeAttributes; -})(); + return blockExecute; +}; + +/* + * EXPORTS_START + * EXPORT script_suppressor + * EXPORT sanitize_attributes + * EXPORTS_END + */ diff --git a/content/main.js b/content/main.js index 2a46c7e..d55ee2e 100644 --- a/content/main.js +++ b/content/main.js @@ -5,113 +5,133 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; +/* + * IMPORTS_START + * IMPORT handle_page_actions + * IMPORT url_item + * IMPORT gen_unique + * IMPORT sanitize_attributes + * IMPORT script_suppressor + * IMPORT is_chrome + * IMPORT is_mozilla + * IMPORTS_END + */ -(() => { - const handle_page_actions = window.handle_page_actions; - const url_item = window.url_item; - const gen_unique = window.gen_unique; - const sanitize_attributes = window.sanitize_attributes; +/* + * Due to some technical limitations the chosen method of whitelisting sites + * is to smuggle whitelist indicator in page's url as a "magical" string + * after '#'. Right now this is not needed in HTTP(s) pages where native + * script blocking happens through CSP header injection but is needed for + * protocols like ftp:// and file://. + * + * The code that actually injects the magical string into ftp:// and file:// + * urls has not yet been added to the extension. + */ - /* - * Due to some technical limitations the chosen method of whitelisting sites - * is to smuggle whitelist indicator in page's url as a "magical" string - * after '#'. Right now this is not needed in HTTP(s) pages where native - * script blocking happens through CSP header injection but is needed for - * protocols like ftp:// and file://. - * - * The code that actually injects the magical string into ftp:// and file:// - * urls has not yet been added to the extension. - */ +let url = url_item(document.URL); +let unique = gen_unique(url); +let nonce = unique.substring(1); - let url = url_item(document.URL); - let unique = gen_unique(url); - let nonce = unique.substring(1); - - const scriptSuppressor = window.scriptSuppressor(nonce); - - function needs_blocking() - { - if (url.startsWith("https://") || url.startsWith("http://")) - return false; - - let url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/; - let match = url_re.exec(document.URL); - let base_url = match[1]; - let first_target = match[3]; - let second_target = match[4]; - - if (first_target !== undefined && - first_target === unique) { - if (second_target !== undefined) - window.location.href = base_url + second_target; - else - history.replaceState(null, "", base_url); - - console.log(["allowing whitelisted", document.URL]); - return false; - } - - console.log(["disallowing", document.URL]); - return true; - } +const suppressor = script_suppressor(nonce); + +function needs_blocking() +{ + if (url.startsWith("https://") || url.startsWith("http://")) + return false; - function handle_mutation(mutations, observer) - { - if (document.readyState === 'complete') { - console.log("complete"); - observer.disconnect(); - return; - } - for (let mutation of mutations) { - for (let node of mutation.addedNodes) { - /* - * Modifying - + _POPUPSCRIPTS_ diff --git a/html/display-panel.js b/html/display-panel.js index 9bd683c..4a4cdcd 100644 --- a/html/display-panel.js +++ b/html/display-panel.js @@ -5,11 +5,11 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - -(() => { - const browser = window.browser; +/* + * IMPORTS_START + * IMPORT browser + * IMPORTS_END + */ - document.getElementById("settings_but") - .addEventListener("click", (e) => browser.runtime.openOptionsPage()); -})(); +document.getElementById("settings_but") + .addEventListener("click", (e) => browser.runtime.openOptionsPage()); diff --git a/html/options.html b/html/options.html index c380fc4..03978c7 100644 --- a/html/options.html +++ b/html/options.html @@ -249,13 +249,6 @@ - - - - - - - + _OPTIONSSCRIPTS_ diff --git a/html/options_main.js b/html/options_main.js index ef01e60..6f203fa 100644 --- a/html/options_main.js +++ b/html/options_main.js @@ -5,746 +5,746 @@ * Redistribution terms are gathered in the `copyright' file. */ -"use strict"; - -(() => { - const get_storage = window.get_storage; - const TYPE_PREFIX = window.TYPE_PREFIX; - const TYPE_NAME = window.TYPE_NAME; - const list_prefixes = window.list_prefixes; - - var storage; - function by_id(id) - { - return document.getElementById(id); - } - - function nice_name(prefix, name) - { - return `${name} (${TYPE_NAME[prefix]})`; - } - - const item_li_template = by_id("item_li_template"); - const bag_component_li_template = by_id("bag_component_li_template"); - const chbx_component_li_template = by_id("chbx_component_li_template"); - const radio_component_li_template = by_id("radio_component_li_template"); - const import_li_template = by_id("import_li_template"); - /* Make sure they are later cloned without id. */ - item_li_template.removeAttribute("id"); - bag_component_li_template.removeAttribute("id"); - chbx_component_li_template.removeAttribute("id"); - radio_component_li_template.removeAttribute("id"); - import_li_template.removeAttribute("id"); - - function item_li_id(prefix, item) - { - return `li_${prefix}_${item}`; - } - - /* Insert into list of bags/pages/scripts */ - function add_li(prefix, item, at_the_end=false) - { - let ul = ul_by_prefix[prefix]; - let li = item_li_template.cloneNode(true); - li.id = item_li_id(prefix, item); - - let span = li.firstElementChild; - span.textContent = item; - - let edit_button = span.nextElementSibling; - edit_button.addEventListener("click", () => edit_item(prefix, item)); - - let remove_button = edit_button.nextElementSibling; - remove_button.addEventListener("click", - () => storage.remove(prefix, item)); - - let export_button = remove_button.nextElementSibling; - export_button.addEventListener("click", - () => export_item(prefix, item)); +/* + * IMPORTS_START + * IMPORT get_remote_storage + * IMPORT TYPE_PREFIX + * IMPORT TYPE_NAME + * IMPORT list_prefixes + * IMPORTS_END + */ - if (!at_the_end) { - for (let element of ul.ul.children) { - if (element.id < li.id || element.id.startsWith("work_")) - continue; +var storage; +function by_id(id) +{ + return document.getElementById(id); +} + +function nice_name(prefix, name) +{ + return `${name} (${TYPE_NAME[prefix]})`; +} + +const item_li_template = by_id("item_li_template"); +const bag_component_li_template = by_id("bag_component_li_template"); +const chbx_component_li_template = by_id("chbx_component_li_template"); +const radio_component_li_template = by_id("radio_component_li_template"); +const import_li_template = by_id("import_li_template"); +/* Make sure they are later cloned without id. */ +item_li_template.removeAttribute("id"); +bag_component_li_template.removeAttribute("id"); +chbx_component_li_template.removeAttribute("id"); +radio_component_li_template.removeAttribute("id"); +import_li_template.removeAttribute("id"); + +function item_li_id(prefix, item) +{ + return `li_${prefix}_${item}`; +} + +/* Insert into list of bags/pages/scripts */ +function add_li(prefix, item, at_the_end=false) +{ + let ul = ul_by_prefix[prefix]; + let li = item_li_template.cloneNode(true); + li.id = item_li_id(prefix, item); + + let span = li.firstElementChild; + span.textContent = item; + + let edit_button = span.nextElementSibling; + edit_button.addEventListener("click", () => edit_item(prefix, item)); + + let remove_button = edit_button.nextElementSibling; + remove_button.addEventListener("click", + () => storage.remove(prefix, item)); + + let export_button = remove_button.nextElementSibling; + export_button.addEventListener("click", + () => export_item(prefix, item)); + + if (!at_the_end) { + for (let element of ul.ul.children) { + if (element.id < li.id || element.id.startsWith("work_")) + continue; - ul.ul.insertBefore(li, element); - return; - } + ul.ul.insertBefore(li, element); + return; } - - ul.ul.appendChild(li); } - const chbx_components_ul = by_id("chbx_components_ul"); - const radio_components_ul = by_id("radio_components_ul"); + ul.ul.appendChild(li); +} - function chbx_li_id(prefix, item) - { - return `cli_${prefix}_${item}`; - } +const chbx_components_ul = by_id("chbx_components_ul"); +const radio_components_ul = by_id("radio_components_ul"); - function radio_li_id(prefix, item) - { - return `rli_${prefix}_${item}`; - } +function chbx_li_id(prefix, item) +{ + return `cli_${prefix}_${item}`; +} - //TODO: refactor the 2 functions below +function radio_li_id(prefix, item) +{ + return `rli_${prefix}_${item}`; +} - function add_chbx_li(prefix, name) - { - if (prefix === TYPE_PREFIX.PAGE) - return; +//TODO: refactor the 2 functions below - let li = chbx_component_li_template.cloneNode(true); - li.id = chbx_li_id(prefix, name); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - - let chbx = li.firstElementChild; - let span = chbx.nextElementSibling; - - span.textContent = nice_name(prefix, name); - - chbx_components_ul.appendChild(li); - } - - var radio_component_none_li = by_id("radio_component_none_li"); +function add_chbx_li(prefix, name) +{ + if (prefix === TYPE_PREFIX.PAGE) + return; - function add_radio_li(prefix, name) - { - if (prefix === TYPE_PREFIX.PAGE) - return; + let li = chbx_component_li_template.cloneNode(true); + li.id = chbx_li_id(prefix, name); + li.setAttribute("data-prefix", prefix); + li.setAttribute("data-name", name); - let li = radio_component_li_template.cloneNode(true); - li.id = radio_li_id(prefix, name); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); + let chbx = li.firstElementChild; + let span = chbx.nextElementSibling; - let radio = li.firstElementChild; - let span = radio.nextElementSibling; + span.textContent = nice_name(prefix, name); - span.textContent = nice_name(prefix, name); + chbx_components_ul.appendChild(li); +} - radio_components_ul.insertBefore(li, radio_component_none_li); - } - - const page_payload_span = by_id("page_payload"); - - function set_page_components(components) - { - if (components === undefined) { - page_payload_span.setAttribute("data-payload", "no"); - page_payload_span.textContent = "(None)"; - } else { - page_payload_span.setAttribute("data-payload", "yes"); - let [prefix, name] = components; - page_payload_span.setAttribute("data-prefix", prefix); - page_payload_span.setAttribute("data-name", name); - page_payload_span.textContent = nice_name(prefix, name); - } - } +var radio_component_none_li = by_id("radio_component_none_li"); - const page_allow_chbx = by_id("page_allow_chbx"); +function add_radio_li(prefix, name) +{ + if (prefix === TYPE_PREFIX.PAGE) + return; - /* Used to reset edited page. */ - function reset_work_page_li(ul, item, settings) - { - ul.work_name_input.value = maybe_string(item); - settings = settings || {allow: false, components: undefined}; - page_allow_chbx.checked = !!settings.allow; + let li = radio_component_li_template.cloneNode(true); + li.id = radio_li_id(prefix, name); + li.setAttribute("data-prefix", prefix); + li.setAttribute("data-name", name); - set_page_components(settings.components); - } + let radio = li.firstElementChild; + let span = radio.nextElementSibling; - function work_page_li_components() - { - if (page_payload_span.getAttribute("data-payload") === "no") - return undefined; + span.textContent = nice_name(prefix, name); - let prefix = page_payload_span.getAttribute("data-prefix"); - let name = page_payload_span.getAttribute("data-name"); - return [prefix, name]; - } + radio_components_ul.insertBefore(li, radio_component_none_li); +} - /* Used to get edited page data for saving. */ - function work_page_li_data(ul) - { - let url = ul.work_name_input.value; - let settings = { - components : work_page_li_components(), - allow : !!page_allow_chbx.checked - }; +const page_payload_span = by_id("page_payload"); - return [url, settings]; +function set_page_components(components) +{ + if (components === undefined) { + page_payload_span.setAttribute("data-payload", "no"); + page_payload_span.textContent = "(None)"; + } else { + page_payload_span.setAttribute("data-payload", "yes"); + let [prefix, name] = components; + page_payload_span.setAttribute("data-prefix", prefix); + page_payload_span.setAttribute("data-name", name); + page_payload_span.textContent = nice_name(prefix, name); } +} - const empty_bag_component_li = by_id("empty_bag_component_li"); - var bag_components_ul = by_id("bag_components_ul"); - - /* Used to construct and update components list of edited bag. */ - function add_bag_components(components) - { - for (let component of components) { - let [prefix, name] = component; - let li = bag_component_li_template.cloneNode(true); - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - let span = li.firstElementChild; - span.textContent = nice_name(prefix, name); - let remove_but = span.nextElementSibling; - remove_but.addEventListener("click", () => - bag_components_ul.removeChild(li)); - bag_components_ul.appendChild(li); - } - - bag_components_ul.appendChild(empty_bag_component_li); - } +const page_allow_chbx = by_id("page_allow_chbx"); - /* Used to reset edited bag. */ - function reset_work_bag_li(ul, item, components) - { - components = components || []; +/* Used to reset edited page. */ +function reset_work_page_li(ul, item, settings) +{ + ul.work_name_input.value = maybe_string(item); + settings = settings || {allow: false, components: undefined}; + page_allow_chbx.checked = !!settings.allow; - ul.work_name_input.value = maybe_string(item); - let old_components_ul = bag_components_ul; - bag_components_ul = old_components_ul.cloneNode(false); + set_page_components(settings.components); +} - ul.work_li.insertBefore(bag_components_ul, old_components_ul); - ul.work_li.removeChild(old_components_ul); +function work_page_li_components() +{ + if (page_payload_span.getAttribute("data-payload") === "no") + return undefined; - add_bag_components(components); - } + let prefix = page_payload_span.getAttribute("data-prefix"); + let name = page_payload_span.getAttribute("data-name"); + return [prefix, name]; +} - /* Used to get edited bag data for saving. */ - function work_bag_li_data(ul) - { - let components_ul = ul.work_name_input.nextElementSibling; - let component_li = components_ul.firstElementChild; +/* Used to get edited page data for saving. */ +function work_page_li_data(ul) +{ + let url = ul.work_name_input.value; + let settings = { + components : work_page_li_components(), + allow : !!page_allow_chbx.checked + }; - let components = []; + return [url, settings]; +} - /* Last list element is empty li with id set. */ - while (component_li.id === '') { - components.push([component_li.getAttribute("data-prefix"), - component_li.getAttribute("data-name")]); - component_li = component_li.nextElementSibling; - } +const empty_bag_component_li = by_id("empty_bag_component_li"); +var bag_components_ul = by_id("bag_components_ul"); - return [ul.work_name_input.value, components]; +/* Used to construct and update components list of edited bag. */ +function add_bag_components(components) +{ + for (let component of components) { + let [prefix, name] = component; + let li = bag_component_li_template.cloneNode(true); + li.setAttribute("data-prefix", prefix); + li.setAttribute("data-name", name); + let span = li.firstElementChild; + span.textContent = nice_name(prefix, name); + let remove_but = span.nextElementSibling; + remove_but.addEventListener("click", () => + bag_components_ul.removeChild(li)); + bag_components_ul.appendChild(li); } - const script_url_input = by_id("script_url_field"); - const script_sha256_input = by_id("script_sha256_field"); - const script_contents_field = by_id("script_contents_field"); + bag_components_ul.appendChild(empty_bag_component_li); +} - function maybe_string(maybe_defined) - { - return maybe_defined === undefined ? "" : maybe_defined + ""; - } +/* Used to reset edited bag. */ +function reset_work_bag_li(ul, item, components) +{ + components = components || []; - /* Used to reset edited script. */ - function reset_work_script_li(ul, name, data) - { - ul.work_name_input.value = maybe_string(name); - if (data === undefined) - data = {}; - script_url_input.value = maybe_string(data.url); - script_sha256_input.value = maybe_string(data.hash); - script_contents_field.value = maybe_string(data.text); - } + ul.work_name_input.value = maybe_string(item); + let old_components_ul = bag_components_ul; + bag_components_ul = old_components_ul.cloneNode(false); - /* Used to get edited script data for saving. */ - function work_script_li_data(ul) - { - return [ul.work_name_input.value, { - url : script_url_input.value, - hash : script_sha256_input.value, - text : script_contents_field.value - }]; - } + ul.work_li.insertBefore(bag_components_ul, old_components_ul); + ul.work_li.removeChild(old_components_ul); - function cancel_work(prefix) - { - let ul = ul_by_prefix[prefix]; + add_bag_components(components); +} - if (ul.state === UL_STATE.IDLE) - return; +/* Used to get edited bag data for saving. */ +function work_bag_li_data(ul) +{ + let components_ul = ul.work_name_input.nextElementSibling; + let component_li = components_ul.firstElementChild; - if (ul.state === UL_STATE.EDITING_ENTRY) { - add_li(prefix, ul.edited_item); - } + let components = []; - ul.work_li.classList.add("hide"); - ul.state = UL_STATE.IDLE; + /* Last list element is empty li with id set. */ + while (component_li.id === '') { + components.push([component_li.getAttribute("data-prefix"), + component_li.getAttribute("data-name")]); + component_li = component_li.nextElementSibling; } - function save_work(prefix) - { - let ul = ul_by_prefix[prefix]; + return [ul.work_name_input.value, components]; +} - if (ul.state === UL_STATE.IDLE) - return; +const script_url_input = by_id("script_url_field"); +const script_sha256_input = by_id("script_sha256_field"); +const script_contents_field = by_id("script_contents_field"); - let [item, data] = ul.get_work_li_data(ul); +function maybe_string(maybe_defined) +{ + return maybe_defined === undefined ? "" : maybe_defined + ""; +} - /* Here we fire promises and return without waiting. */ +/* Used to reset edited script. */ +function reset_work_script_li(ul, name, data) +{ + ul.work_name_input.value = maybe_string(name); + if (data === undefined) + data = {}; + script_url_input.value = maybe_string(data.url); + script_sha256_input.value = maybe_string(data.hash); + script_contents_field.value = maybe_string(data.text); +} - if (ul.state === UL_STATE.EDITING_ENTRY) - storage.replace(prefix, ul.edited_item, item, data); - if (ul.state === UL_STATE.ADDING_ENTRY) - storage.set(prefix, item, data); +/* Used to get edited script data for saving. */ +function work_script_li_data(ul) +{ + return [ul.work_name_input.value, { + url : script_url_input.value, + hash : script_sha256_input.value, + text : script_contents_field.value + }]; +} - cancel_work(prefix); - } +function cancel_work(prefix) +{ + let ul = ul_by_prefix[prefix]; - function edit_item(prefix, item) - { - cancel_work(prefix); + if (ul.state === UL_STATE.IDLE) + return; - let ul = ul_by_prefix[prefix]; - let li = by_id(item_li_id(prefix, item)); - ul.reset_work_li(ul, item, storage.get(prefix, item)); - ul.ul.insertBefore(ul.work_li, li); - ul.ul.removeChild(li); - ul.work_li.classList.remove("hide"); - - ul.state = UL_STATE.EDITING_ENTRY; - ul.edited_item = item; + if (ul.state === UL_STATE.EDITING_ENTRY) { + add_li(prefix, ul.edited_item); } - const file_downloader = by_id("file_downloader"); + ul.work_li.classList.add("hide"); + ul.state = UL_STATE.IDLE; +} - function recursively_export_item(prefix, name, added_items, items_data) - { - let key = prefix + name; +function save_work(prefix) +{ + let ul = ul_by_prefix[prefix]; - if (added_items.has(key)) - return; + if (ul.state === UL_STATE.IDLE) + return; - let data = storage.get(prefix, name); - if (data === undefined) { - console.log(`${TYPE_NAME[prefix]} '${name}' for export not found`); - return; - } + let [item, data] = ul.get_work_li_data(ul); - if (prefix !== TYPE_PREFIX.SCRIPT) { - let components = prefix === TYPE_PREFIX.BAG ? - data : [data.components]; + /* Here we fire promises and return without waiting. */ - for (let [comp_prefix, comp_name] of components) { - recursively_export_item(comp_prefix, comp_name, - added_items, items_data); - } - } + if (ul.state === UL_STATE.EDITING_ENTRY) + storage.replace(prefix, ul.edited_item, item, data); + if (ul.state === UL_STATE.ADDING_ENTRY) + storage.set(prefix, item, data); - items_data.push({[key]: data}); - added_items.add(key); - } + cancel_work(prefix); +} - function export_item(prefix, name) - { - let added_items = new Set(); - let items_data = []; - recursively_export_item(prefix, name, added_items, items_data); - let file = new Blob([JSON.stringify(items_data)], - {type: "application/json"}); - let url = URL.createObjectURL(file); - file_downloader.setAttribute("href", url); - file_downloader.setAttribute("download", prefix + name + ".json"); - file_downloader.click(); - file_downloader.removeAttribute("href"); - URL.revokeObjectURL(url); - } +function edit_item(prefix, item) +{ + cancel_work(prefix); - function add_new_item(prefix) - { - cancel_work(prefix); + let ul = ul_by_prefix[prefix]; + let li = by_id(item_li_id(prefix, item)); + ul.reset_work_li(ul, item, storage.get(prefix, item)); + ul.ul.insertBefore(ul.work_li, li); + ul.ul.removeChild(li); + ul.work_li.classList.remove("hide"); - let ul = ul_by_prefix[prefix]; - ul.reset_work_li(ul); - ul.work_li.classList.remove("hide"); - ul.ul.appendChild(ul.work_li); + ul.state = UL_STATE.EDITING_ENTRY; + ul.edited_item = item; +} - ul.state = UL_STATE.ADDING_ENTRY; - } +const file_downloader = by_id("file_downloader"); - const chbx_components_window = by_id("chbx_components_window"); +function recursively_export_item(prefix, name, added_items, items_data) +{ + let key = prefix + name; - function bag_components() - { - chbx_components_window.classList.remove("hide"); - radio_components_window.classList.add("hide"); + if (added_items.has(key)) + return; - for (let li of chbx_components_ul.children) { - let chbx = li.firstElementChild; - chbx.checked = false; - } + let data = storage.get(prefix, name); + if (data === undefined) { + console.log(`${TYPE_NAME[prefix]} '${name}' for export not found`); + return; } - function commit_bag_components() - { - let selected = []; - - for (let li of chbx_components_ul.children) { - let chbx = li.firstElementChild; - if (!chbx.checked) - continue; - - selected.push([li.getAttribute("data-prefix"), - li.getAttribute("data-name")]); + if (prefix !== TYPE_PREFIX.SCRIPT) { + let components = prefix === TYPE_PREFIX.BAG ? + data : [data.components]; + + for (let [comp_prefix, comp_name] of components) { + recursively_export_item(comp_prefix, comp_name, + added_items, items_data); } - - add_bag_components(selected); - cancel_components(); } - const radio_components_window = by_id("radio_components_window"); - var radio_component_none_input = by_id("radio_component_none_input"); - - function page_components() - { - radio_components_window.classList.remove("hide"); - chbx_components_window.classList.add("hide"); + items_data.push({[key]: data}); + added_items.add(key); +} + +function export_item(prefix, name) +{ + let added_items = new Set(); + let items_data = []; + recursively_export_item(prefix, name, added_items, items_data); + let file = new Blob([JSON.stringify(items_data)], + {type: "application/json"}); + let url = URL.createObjectURL(file); + file_downloader.setAttribute("href", url); + file_downloader.setAttribute("download", prefix + name + ".json"); + file_downloader.click(); + file_downloader.removeAttribute("href"); + URL.revokeObjectURL(url); +} + +function add_new_item(prefix) +{ + cancel_work(prefix); + + let ul = ul_by_prefix[prefix]; + ul.reset_work_li(ul); + ul.work_li.classList.remove("hide"); + ul.ul.appendChild(ul.work_li); + + ul.state = UL_STATE.ADDING_ENTRY; +} + +const chbx_components_window = by_id("chbx_components_window"); + +function bag_components() +{ + chbx_components_window.classList.remove("hide"); + radio_components_window.classList.add("hide"); + + for (let li of chbx_components_ul.children) { + let chbx = li.firstElementChild; + chbx.checked = false; + } +} - radio_component_none_input.checked = true; +function commit_bag_components() +{ + let selected = []; - let components = work_page_li_components(); - if (components === undefined) - return; + for (let li of chbx_components_ul.children) { + let chbx = li.firstElementChild; + if (!chbx.checked) + continue; - let [prefix, item] = components; - let li = by_id(radio_li_id(prefix, item)); - if (li === null) - radio_component_none_input.checked = false; - else - li.firstElementChild.checked = true; + selected.push([li.getAttribute("data-prefix"), + li.getAttribute("data-name")]); } - function commit_page_components() - { - let components = null; + add_bag_components(selected); + cancel_components(); +} - for (let li of radio_components_ul.children) { - let radio = li.firstElementChild; - if (!radio.checked) - continue; +const radio_components_window = by_id("radio_components_window"); +var radio_component_none_input = by_id("radio_component_none_input"); - components = [li.getAttribute("data-prefix"), - li.getAttribute("data-name")]; +function page_components() +{ + radio_components_window.classList.remove("hide"); + chbx_components_window.classList.add("hide"); - if (radio.id === "radio_component_none_input") - components = undefined; + radio_component_none_input.checked = true; - break; - } + let components = work_page_li_components(); + if (components === undefined) + return; - if (components !== null) - set_page_components(components); - cancel_components(); - } + let [prefix, item] = components; + let li = by_id(radio_li_id(prefix, item)); + if (li === null) + radio_component_none_input.checked = false; + else + li.firstElementChild.checked = true; +} - function cancel_components() - { - chbx_components_window.classList.add("hide"); - radio_components_window.classList.add("hide"); - } +function commit_page_components() +{ + let components = null; - const UL_STATE = { - EDITING_ENTRY : 0, - ADDING_ENTRY : 1, - IDLE : 2 - }; - - const ul_by_prefix = { - [TYPE_PREFIX.PAGE] : { - ul : by_id("pages_ul"), - work_li : by_id("work_page_li"), - work_name_input : by_id("page_url_field"), - reset_work_li : reset_work_page_li, - get_work_li_data : work_page_li_data, - select_components : page_components, - commit_components : commit_page_components, - state : UL_STATE.IDLE, - edited_item : undefined, - }, - [TYPE_PREFIX.BAG] : { - ul : by_id("bags_ul"), - work_li : by_id("work_bag_li"), - work_name_input : by_id("bag_name_field"), - reset_work_li : reset_work_bag_li, - get_work_li_data : work_bag_li_data, - select_components : bag_components, - commit_components : commit_bag_components, - state : UL_STATE.IDLE, - edited_item : undefined, - }, - [TYPE_PREFIX.SCRIPT] : { - ul : by_id("scripts_ul"), - work_li : by_id("work_script_li"), - work_name_input : by_id("script_name_field"), - reset_work_li : reset_work_script_li, - get_work_li_data : work_script_li_data, - state : UL_STATE.IDLE, - edited_item : undefined, - } - } + for (let li of radio_components_ul.children) { + let radio = li.firstElementChild; + if (!radio.checked) + continue; + + components = [li.getAttribute("data-prefix"), + li.getAttribute("data-name")]; + + if (radio.id === "radio_component_none_input") + components = undefined; + + break; + } + + if (components !== null) + set_page_components(components); + cancel_components(); +} + +function cancel_components() +{ + chbx_components_window.classList.add("hide"); + radio_components_window.classList.add("hide"); +} + +const UL_STATE = { + EDITING_ENTRY : 0, + ADDING_ENTRY : 1, + IDLE : 2 +}; + +const ul_by_prefix = { + [TYPE_PREFIX.PAGE] : { + ul : by_id("pages_ul"), + work_li : by_id("work_page_li"), + work_name_input : by_id("page_url_field"), + reset_work_li : reset_work_page_li, + get_work_li_data : work_page_li_data, + select_components : page_components, + commit_components : commit_page_components, + state : UL_STATE.IDLE, + edited_item : undefined, + }, + [TYPE_PREFIX.BAG] : { + ul : by_id("bags_ul"), + work_li : by_id("work_bag_li"), + work_name_input : by_id("bag_name_field"), + reset_work_li : reset_work_bag_li, + get_work_li_data : work_bag_li_data, + select_components : bag_components, + commit_components : commit_bag_components, + state : UL_STATE.IDLE, + edited_item : undefined, + }, + [TYPE_PREFIX.SCRIPT] : { + ul : by_id("scripts_ul"), + work_li : by_id("work_script_li"), + work_name_input : by_id("script_name_field"), + reset_work_li : reset_work_script_li, + get_work_li_data : work_script_li_data, + state : UL_STATE.IDLE, + edited_item : undefined, + } +} + +const import_window = by_id("import_window"); +const import_loading_radio = by_id("import_loading_radio"); +const import_failed_radio = by_id("import_failed_radio"); +const import_selection_radio = by_id("import_selection_radio"); +const bad_file_errormsg = by_id("bad_file_errormsg"); + +/* + * Newer browsers could utilise `text' method of File objects. + * Older ones require FileReader. + */ - const import_window = by_id("import_window"); - const import_loading_radio = by_id("import_loading_radio"); - const import_failed_radio = by_id("import_failed_radio"); - const import_selection_radio = by_id("import_selection_radio"); - const bad_file_errormsg = by_id("bad_file_errormsg"); +function _read_file(file, resolve, reject) +{ + let reader = new FileReader(); + + reader.onload = () => resolve(reader.result); + reader.onerror = () => reject(reader.error); + reader.readAsText(file); +} + +function read_file(file) +{ + return new Promise((resolve, reject) => + _read_file(file, resolve, reject)); +} + +async function import_from_file(event) +{ + let files = event.target.files; + if (files.length < 1) + return; + + import_window.classList.remove("hide"); + import_loading_radio.checked = true; + + let result = undefined; + + try { + result = JSON.parse(await read_file(files[0])); + } catch(e) { + bad_file_errormsg.textContent = "" + e; + import_failed_radio.checked = true; + return; + } + + let errormsg = validate_settings(result); + if (errormsg !== false) { + bad_file_errormsg.textContent = errormsg; + import_failed_radio.checked = true; + return; + } + + populate_import_list(result); + import_selection_radio.checked = true; +} + +function validate_settings(settings) +{ + // TODO + return false; +} + +function import_li_id(prefix, item) +{ + return `ili_${prefix}_${item}`; +} + +let import_ul = by_id("import_ul"); +let import_chbxs_colliding = undefined; +let settings_import_map = undefined; + +function populate_import_list(settings) +{ + let old_children = import_ul.children; + while (old_children[0] !== undefined) + import_ul.removeChild(old_children[0]); + + import_chbxs_colliding = []; + settings_import_map = new Map(); + + for (let setting of settings) { + let [key, value] = Object.entries(setting)[0]; + let prefix = key[0]; + let name = key.substring(1); + add_import_li(prefix, name); + settings_import_map.set(key, value); + } +} + +function add_import_li(prefix, name) +{ + let li = import_li_template.cloneNode(true); + let name_span = li.firstElementChild; + let chbx = name_span.nextElementSibling; + let warning_span = chbx.nextElementSibling; + + li.setAttribute("data-prefix", prefix); + li.setAttribute("data-name", name); + li.id = import_li_id(prefix, name); + name_span.textContent = nice_name(prefix, name); + + if (storage.get(prefix, name) !== undefined) { + import_chbxs_colliding.push(chbx); + warning_span.textContent = "(will overwrite existing setting!)"; + } + + import_ul.appendChild(li); +} + +function check_all_imports() +{ + for (let li of import_ul.children) + li.firstElementChild.nextElementSibling.checked = true; +} + +function uncheck_all_imports() +{ + for (let li of import_ul.children) + li.firstElementChild.nextElementSibling.checked = false; +} + +function uncheck_colliding_imports() +{ + for (let chbx of import_chbxs_colliding) + chbx.checked = false; +} + +const file_opener_form = by_id("file_opener_form"); + +function hide_import_window() +{ + import_window.classList.add("hide"); + /* Let GC free some memory */ + import_chbxs_colliding = undefined; + settings_import_map = undefined; /* - * Newer browsers could utilise `text' method of File objects. - * Older ones require FileReader. + * Reset file . Without this, a second attempt to import the same + * file would result in "change" event on happening on element. */ + file_opener_form.reset(); +} - function _read_file(file, resolve, reject) - { - let reader = new FileReader(); +function commit_import() +{ + for (let li of import_ul.children) { + let chbx = li.firstElementChild.nextElementSibling; - reader.onload = () => resolve(reader.result); - reader.onerror = () => reject(reader.error); - reader.readAsText(file); - } - - function read_file(file) - { - return new Promise((resolve, reject) => - _read_file(file, resolve, reject)); - } + if (!chbx.checked) + continue; - async function import_from_file(event) - { - let files = event.target.files; - if (files.length < 1) - return; - - import_window.classList.remove("hide"); - import_loading_radio.checked = true; - - let result = undefined; - - try { - result = JSON.parse(await read_file(files[0])); - } catch(e) { - bad_file_errormsg.textContent = "" + e; - import_failed_radio.checked = true; - return; - } - - let errormsg = validate_settings(result); - if (errormsg !== false) { - bad_file_errormsg.textContent = errormsg; - import_failed_radio.checked = true; - return; + let prefix = li.getAttribute("data-prefix"); + let name = li.getAttribute("data-name"); + let key = prefix + name; + let value = settings_import_map.get(key); + storage.set(prefix, name, value); + } + + hide_import_window(); +} + +function initialize_import_facility() +{ + let import_but = by_id("import_but"); + let file_opener = by_id("file_opener"); + let import_failok_but = by_id("import_failok_but"); + let check_all_import_but = by_id("check_all_import_but"); + let uncheck_all_import_but = by_id("uncheck_all_import_but"); + let uncheck_existing_import_but = by_id("uncheck_existing_import_but"); + let commit_import_but = by_id("commit_import_but"); + let cancel_import_but = by_id("cancel_import_but"); + import_but.addEventListener("click", () => file_opener.click()); + file_opener.addEventListener("change", import_from_file); + import_failok_but.addEventListener("click", hide_import_window); + check_all_import_but.addEventListener("click", check_all_imports); + uncheck_all_import_but.addEventListener("click", uncheck_all_imports); + uncheck_colliding_import_but + .addEventListener("click", uncheck_colliding_imports); + commit_import_but.addEventListener("click", commit_import); + cancel_import_but.addEventListener("click", hide_import_window); +} + +async function main() +{ + storage = await get_remote_storage(); + + for (let prefix of list_prefixes) { + for (let item of storage.get_all_names(prefix).sort()) { + add_li(prefix, item, true); + add_chbx_li(prefix, item); + add_radio_li(prefix, item); } - populate_import_list(result); - import_selection_radio.checked = true; - } - - function validate_settings(settings) - { - // TODO - return false; - } + let name = TYPE_NAME[prefix]; - function import_li_id(prefix, item) - { - return `ili_${prefix}_${item}`; - } + let add_but = by_id(`add_${name}_but`); + let discard_but = by_id(`discard_${name}_but`); + let save_but = by_id(`save_${name}_but`); - let import_ul = by_id("import_ul"); - let import_chbxs_colliding = undefined; - let settings_import_map = undefined; - - function populate_import_list(settings) - { - let old_children = import_ul.children; - while (old_children[0] !== undefined) - import_ul.removeChild(old_children[0]); - - import_chbxs_colliding = []; - settings_import_map = new Map(); - - for (let setting of settings) { - let [key, value] = Object.entries(setting)[0]; - let prefix = key[0]; - let name = key.substring(1); - add_import_li(prefix, name); - settings_import_map.set(key, value); - } - } + add_but.addEventListener("click", () => add_new_item(prefix)); + discard_but.addEventListener("click", () => cancel_work(prefix)); + save_but.addEventListener("click", () => save_work(prefix)); - function add_import_li(prefix, name) - { - let li = import_li_template.cloneNode(true); - let name_span = li.firstElementChild; - let chbx = name_span.nextElementSibling; - let warning_span = chbx.nextElementSibling; + if (prefix === TYPE_PREFIX.SCRIPT) + continue; - li.setAttribute("data-prefix", prefix); - li.setAttribute("data-name", name); - li.id = import_li_id(prefix, name); - name_span.textContent = nice_name(prefix, name); - - if (storage.get(prefix, name) !== undefined) { - import_chbxs_colliding.push(chbx); - warning_span.textContent = "(will overwrite existing setting!)"; - } + let ul = ul_by_prefix[prefix]; - import_ul.appendChild(li); - } + let commit_components_but = by_id(`commit_${name}_components_but`); + let cancel_components_but = by_id(`cancel_${name}_components_but`); + let select_components_but = by_id(`select_${name}_components_but`); - function check_all_imports() - { - for (let li of import_ul.children) - li.firstElementChild.nextElementSibling.checked = true; + commit_components_but + .addEventListener("click", ul.commit_components); + select_components_but + .addEventListener("click", ul.select_components); + cancel_components_but.addEventListener("click", cancel_components); } - function uncheck_all_imports() - { - for (let li of import_ul.children) - li.firstElementChild.nextElementSibling.checked = false; - } - - function uncheck_colliding_imports() - { - for (let chbx of import_chbxs_colliding) - chbx.checked = false; - } + initialize_import_facility(); - const file_opener_form = by_id("file_opener_form"); + storage.add_change_listener(handle_change); +} - function hide_import_window() - { - import_window.classList.add("hide"); - /* Let GC free some memory */ - import_chbxs_colliding = undefined; - settings_import_map = undefined; +function handle_change(change) +{ + if (change.old_val === undefined) { + add_li(change.prefix, change.item); + add_chbx_li(change.prefix, change.item); + add_radio_li(change.prefix, change.item); - /* - * Reset file . Without this, a second attempt to import the same - * file would result in "change" event on happening on element. - */ - file_opener_form.reset(); + return; } - function commit_import() - { - for (let li of import_ul.children) { - let chbx = li.firstElementChild.nextElementSibling; - - if (!chbx.checked) - continue; - - let prefix = li.getAttribute("data-prefix"); - let name = li.getAttribute("data-name"); - let key = prefix + name; - let value = settings_import_map.get(key); - storage.set(prefix, name, value); - } - - hide_import_window(); - } + if (change.new_val !== undefined) + return; - function initialize_import_facility() - { - let import_but = by_id("import_but"); - let file_opener = by_id("file_opener"); - let import_failok_but = by_id("import_failok_but"); - let check_all_import_but = by_id("check_all_import_but"); - let uncheck_all_import_but = by_id("uncheck_all_import_but"); - let uncheck_existing_import_but = by_id("uncheck_existing_import_but"); - let commit_import_but = by_id("commit_import_but"); - let cancel_import_but = by_id("cancel_import_but"); - import_but.addEventListener("click", () => file_opener.click()); - file_opener.addEventListener("change", import_from_file); - import_failok_but.addEventListener("click", hide_import_window); - check_all_import_but.addEventListener("click", check_all_imports); - uncheck_all_import_but.addEventListener("click", uncheck_all_imports); - uncheck_colliding_import_but - .addEventListener("click", uncheck_colliding_imports); - commit_import_but.addEventListener("click", commit_import); - cancel_import_but.addEventListener("click", hide_import_window); + let ul = ul_by_prefix[change.prefix]; + if (ul.state === UL_STATE.EDITING_ENTRY && + ul.edited_item === change.item) { + ul.state = UL_STATE.ADDING_ENTRY; + return; } - async function main() - { - storage = await get_storage(); - - for (let prefix of list_prefixes) { - for (let item of storage.get_all_names(prefix).sort()) { - add_li(prefix, item, true); - add_chbx_li(prefix, item); - add_radio_li(prefix, item); - } - - let name = TYPE_NAME[prefix]; - - let add_but = by_id(`add_${name}_but`); - let discard_but = by_id(`discard_${name}_but`); - let save_but = by_id(`save_${name}_but`); - - add_but.addEventListener("click", () => add_new_item(prefix)); - discard_but.addEventListener("click", () => cancel_work(prefix)); - save_but.addEventListener("click", () => save_work(prefix)); - - if (prefix === TYPE_PREFIX.SCRIPT) - continue; - - let ul = ul_by_prefix[prefix]; - - let commit_components_but = by_id(`commit_${name}_components_but`); - let cancel_components_but = by_id(`cancel_${name}_components_but`); - let select_components_but = by_id(`select_${name}_components_but`); - - commit_components_but - .addEventListener("click", ul.commit_components); - select_components_but - .addEventListener("click", ul.select_components); - cancel_components_but.addEventListener("click", cancel_components); - } - - initialize_import_facility(); + let uls_creators = [[ul.ul, item_li_id]]; - storage.add_change_listener(handle_change); + if (change.prefix !== TYPE_PREFIX.PAGE) { + uls_creators.push([chbx_components_ul, chbx_li_id]); + uls_creators.push([radio_components_ul, radio_li_id]); } - function handle_change(change) - { - if (change.old_val === undefined) { - add_li(change.prefix, change.item); - add_chbx_li(change.prefix, change.item); - add_radio_li(change.prefix, change.item); - - return; - } - - if (change.new_val !== undefined) - return; - - let ul = ul_by_prefix[change.prefix]; - if (ul.state === UL_STATE.EDITING_ENTRY && - ul.edited_item === change.item) { - ul.state = UL_STATE.ADDING_ENTRY; - return; - } - - let uls_creators = [[ul.ul, item_li_id]]; - - if (change.prefix !== TYPE_PREFIX.PAGE) { - uls_creators.push([chbx_components_ul, chbx_li_id]); - uls_creators.push([radio_components_ul, radio_li_id]); - } - - for (let [components_ul, id_creator] of uls_creators) { - let li = by_id(id_creator(change.prefix, change.item)); - components_ul.removeChild(li); - } + for (let [components_ul, id_creator] of uls_creators) { + let li = by_id(id_creator(change.prefix, change.item)); + components_ul.removeChild(li); } +} - main(); -})(); +main(); diff --git a/manifest.json b/manifest.json index 3984dc6..ab74523 100644 --- a/manifest.json +++ b/manifest.json @@ -1,31 +1,12 @@ // Copyright (C) 2021 Wojtek Kosior -// Copyright (C) 2021 jahoti // Redistribution terms are gathered in the `copyright' file. { "manifest_version": 2, "name": "My extension", "short_name": "Myext", - "version": "0.0.0", - - // WARNING!!! - // EACH USER SHOULD REPLACE "key" WITH UNIQUE VALUE!!! - // OTHERWISE SECURITY CAN BE TRIVIALLY COMPROMISED! - // - // A unique key can be generated with: - // $ ssh-keygen -f /path/to/new/key.pem -t rsa -b 1024 - // - // Only relevant to users of chrome-based browsers. - // Users of FireFox forks are safe. - - "key": "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcnNhAAAAAwEAAQAAAIEA+0GT5WNmRRo8e5tL9+BmNtY6aBPwLIgbPnLShYBMSR40iYwLTsccrkwBXb3bs1o4p6q5WJugI8Lsia+GXZc/XHGFkq7D1aWiTxlJLs8z0JC2TQ2/yatYmBMchogYGeeUfP7aI7JJZwpATts+VhIvgga/4FYj+DijMIEpwdckqFEAAAII4Dh7HOA4exwAAAAHc3NoLXJzYQAAAIEA+0GT5WNmRRo8e5tL9+BmNtY6aBPwLIgbPnLShYBMSR40iYwLTsccrkwBXb3bs1o4p6q5WJugI8Lsia+GXZc/XHGFkq7D1aWiTxlJLs8z0JC2TQ2/yatYmBMchogYGeeUfP7aI7JJZwpATts+VhIvgga/4FYj+DijMIEpwdckqFEAAAADAQABAAAAgEHB5/MhEKMFOs8e1cMJ97ZiWubiUPlWpcqyQmauLUj1nspg3JTBh8AWJEVkaxuFgU5gYCHQmRjC6yUdywyziOEkFA4r/WpX4WmbIe+GQHRHhitLN0dgF8N6/fVNOoa5StTdfZqyl23pVXyepoDNjrJFKyupqPMmpwfH5lGr9RwBAAAAQG76HflB/5j8P2YgIYX6dQT4Ei0SqiIjNVy7jFJUQDKSJg/PYkedE02JZJBJPcMYxEJUxXtMgq+upamNILfkmY0AAABBAP4v0O5dqjy16xDDFzb4DPNAcw5Za9KJaXKVkUuKXMNZOKTR0RC/upjNTmttY980RKdIx5zA25dO8cx563bSDIsAAABBAP0MaOpBiai/eRmLqhlthHODa+Mur6W3uc9PyhWhgDBjLNMR/doaYeyfVKxtIiN3a+HkN++G+vbokRweQv++bhMAAAANdXJ6QGxvY2FsaG9zdAECAwQFBg==", + "version": "0.0.0",_CHROMIUM_KEY_ "author": "various", - "description": "Kill the web&js", - "applications": { - "gecko": { - "id": "{6fe13369-88e9-440f-b837-5012fb3bedec}", - "strict_min_version": "60.0" - } - }, + "description": "Kill the web&js",_GECKO_APPLICATIONS_ "icons":{ "64": "icons/myext.png" }, @@ -56,23 +37,7 @@ ], "background": { "persistent": true, - "scripts": [ - "common/stored_types.js", - "common/lock.js", - "common/once.js", - "common/browser.js", - "background/storage.js", - "background/message_server.js", - "common/connection_types.js", - "background/storage_server.js", - "common/url_item.js", - "common/sha256.js", - "background/settings_query.js", - "background/page_actions_server.js", - "common/gen_unique.js", - "background/policy_injector.js", - "background/main.js" - ] + "scripts": [_BGSCRIPTS_] }, "content_scripts": [ { @@ -80,16 +45,7 @@ "matches": [""], "match_about_blank": true, "all_frames": true, - "js": [ - "content/freezer.js", - "common/browser.js", - "common/connection_types.js", - "content/page_actions.js", - "common/url_item.js", - "common/sha256.js", - "common/gen_unique.js", - "content/main.js" - ] + "js": [_CONTENTSCRIPTS_] } ] } -- cgit v1.2.3