Revision 261548ff
Added by koszko about 2 years ago
| README.txt | ||
|---|---|---|
| 6 | 6 |
without executing nonfree software. |
| 7 | 7 |
|
| 8 | 8 |
Currently, the target browsers for this extension are Ungoogled Chromium |
| 9 |
and various forks of Firefox Quantum (57+).
|
|
| 9 |
and various forks of Firefox (version 60+).
|
|
| 10 | 10 |
|
| 11 | 11 |
This extension is still in an early stage. See TODOS.org. Also see |
| 12 | 12 |
`https://git.koszko.org/browser-extension-doc/' for documentation in |
| 13 | 13 |
development. |
| 14 | 14 |
|
| 15 | 15 |
## Installation ## |
| 16 |
The extension can be loaded into Ungoogled Chromium or a modern Gecko-based |
|
| 17 |
browser as unpacked extension. As of now, project's main directory is also |
|
| 18 |
the extension directory. |
|
| 16 |
The extension can be "built" with `./build.sh mozilla' or `./build.sh chromium'. |
|
| 17 |
This creates directories build_mozilla/ and build_chromium/, respectively. |
|
| 18 |
Such directory can be loaded into Ungoogled Chromium or a modern Gecko-based |
|
| 19 |
browser as unpacked extension. |
|
| 19 | 20 |
|
| 20 | 21 |
## Copyright ## |
| 21 | 22 |
All copyright information is gathered in the `copyright' file which follows |
| TODOS.org | ||
|---|---|---|
| 20 | 20 |
- make script bag components re-orderable (via drag&drop in options page) -- CRUCIAL |
| 21 | 21 |
- find some way not to require each chrome user to modify manifest.json |
| 22 | 22 |
- test with more browser forks (Abrowser, Parabola IceWeasel, LibreWolf) |
| 23 |
- also see if browsers based on pre-quantum FF support enough of |
|
| 24 |
WebExtensions for easy porting |
|
| 25 | 23 |
- make sure page's own csp in <head> doesn't block our scripts |
| 26 |
- find out how and make it possible to whitelist non-https urls and |
|
| 27 |
whether we can inject csp to them |
|
| 28 | 24 |
- create a repository to host scripts |
| 29 | 25 |
- enable the extension to automatically fetch script substitutes from the repo |
| 30 | 26 |
- make it possible to inject scripts to arbitrary places in DOM |
| 31 | 27 |
- make script blocking code omit those scripts |
| 32 | 28 |
- check if prerendering has to be blocked -- CRUCIAL |
| 33 | 29 |
- block prefetch |
| 34 |
- rearrange files in extension, add some mechanism to build the extension |
|
| 35 |
- all solutions to modularize js code SUCK; come up with own simple DSL |
|
| 36 |
to manage imports/exports |
|
| 30 |
- rearrange files in extension |
|
| 31 |
- supplement the build script with a makefile, also produce zipped arifacts |
|
| 37 | 32 |
- perform never-ending refactoring of already-written code |
| 38 | 33 |
- also implement support for whitelisting of non-https urls |
| 39 | 34 |
- validate data entered in settings |
| ... | ... | |
| 49 | 44 |
(unless someone suggests another good name before we do so) |
| 50 | 45 |
- add an option to disable script blocking globally |
| 51 | 46 |
- Add support to settings_query for non-standard URLs |
| 52 |
(e.g. file:// and about:)
|
|
| 47 |
(e.g. file:// and ftp://)
|
|
| 53 | 48 |
- Process HTML files in data: URLs instead of just blocking them |
| 49 |
- improve CSP injection for pathological cases like <script> before <head> |
|
| 50 |
- Fix FF script blocking and whitelisting (FF seems to be by itself repeatedly |
|
| 51 |
injecting CSP headers that were injected once, this makes it impossible to |
|
| 52 |
whielist site that was unwhitelisted before; FF also seems to be removing our |
|
| 53 |
injected script's nonce for no reason 🙁) |
|
| 54 | 54 |
|
| 55 | 55 |
DONE: |
| 56 |
- make blocking more torough -- DONE 2021-06-28 |
|
| 56 |
- find out if we can successfully use CSP to block file:// under FF -- DONE 2021-06-30 |
|
| 57 |
- come up with own simple DSL to manage imports/exports -- DONE 2021-06-30 |
|
| 58 |
- add some mechanism to build the extension -- DONE 2021-06-30 |
|
| 59 |
- see if browsers based on pre-quantum FF support enough of -- DONE 2021-06-29 |
|
| 60 |
WebExtensions for easy porting (no, those we know dropped the support) |
|
| 61 |
- make blocking more thorough -- DONE 2021-06-28 |
|
| 57 | 62 |
- mind the data: urls -- CRUCIAL |
| 58 | 63 |
- employ copyright file in Debian format -- DONE 2021-06-25 |
| 59 | 64 |
- find out what causes storage sometimes not to get initialized under IceCat 60 -- DONE 2021-06-23 |
| background/main.js | ||
|---|---|---|
| 5 | 5 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 6 |
*/ |
| 7 | 7 |
|
| 8 |
"use strict"; |
|
| 9 |
|
|
| 10 |
(() => {
|
|
| 11 |
const TYPE_PREFIX = window.TYPE_PREFIX; |
|
| 12 |
const get_storage = window.get_storage; |
|
| 13 |
const start_storage_server = window.start_storage_server; |
|
| 14 |
const start_page_actions_server = window.start_page_actions_server; |
|
| 15 |
const start_policy_injector = window.start_policy_injector; |
|
| 16 |
const browser = window.browser; |
|
| 17 |
|
|
| 18 |
start_storage_server(); |
|
| 19 |
start_page_actions_server(); |
|
| 20 |
start_policy_injector(); |
|
| 21 |
|
|
| 22 |
async function init_myext(install_details) |
|
| 23 |
{
|
|
| 24 |
console.log("details:", install_details);
|
|
| 25 |
if (install_details.reason != "install") |
|
| 26 |
return; |
|
| 27 |
|
|
| 28 |
let storage = await get_storage(); |
|
| 29 |
|
|
| 30 |
await storage.clear(); |
|
| 31 |
|
|
| 32 |
/* |
|
| 33 |
* Below we add sample settings to the extension. |
|
| 34 |
* Those should be considered example values for viewing in the options |
|
| 35 |
* page. They won't make my.fsf.org work. The only scripts that does |
|
| 36 |
* something useful right now is the opencores one. |
|
| 37 |
*/ |
|
| 38 |
|
|
| 39 |
let components = []; |
|
| 40 |
for (let script_data of [ |
|
| 41 |
{url: "http://127.0.0.1:8000/myfsf_define_CRM.js",
|
|
| 42 |
hash:"bf0cc81c7e8d5f800877b4bc3f14639f946f5ac6d4dc120255ffac5eba5e48fe"}, |
|
| 43 |
{url: "https://my.fsf.org/misc/jquery.js?v=1.4.4",
|
|
| 44 |
hash:"261ae472fa0cbf27c80c9200a1599a60fde581a0e652eee4bf41def8cb61f2d0"}, |
|
| 45 |
{url: "https://my.fsf.org/misc/jquery-extend-3.4.0.js?v=1.4.4",
|
|
| 46 |
hash:"c54103ba57ee210ca55c052e70415402707548a4e6a68dd6efb3895019bee392"}, |
|
| 47 |
{url: "https://my.fsf.org/misc/jquery-html-prefilter-3.5.0-backport.js?v=1.4.4",
|
|
| 48 |
hash:"fad84efa145fb507e5df9b582fa01b1c4e6313de7f72ebdd55726d92fa4dbf06"}, |
|
| 49 |
{url: "https://my.fsf.org/misc/jquery.once.js?v=1.2",
|
|
| 50 |
hash:"1430f42c0d760ba8e05bb3762480502e541f654fec5739ee40625ab22dc38c4f"}, |
|
| 51 |
{url: "https://my.fsf.org/misc/drupal.js?qmaukd",
|
|
| 52 |
hash:"2e08dccbd4d8b728a6871562995a4636b89bfe0ed3b8fb0138191c922228b116"}, |
|
| 53 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/jquery/dist/jquery.min.js?qmaukd",
|
|
| 54 |
hash:"a6d01520d28d15dbe476de84eea90eb3ee2d058722efc062ec73cb5fad78a17b"}, |
|
| 55 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/jquery-ui/jquery-ui.min.js?qmaukd",
|
|
| 56 |
hash:"28ce75d953678c4942df47a11707a15e3c756021cf89090e3e6aa7ad6b6971c3"}, |
|
| 57 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/lodash-compat/lodash.min.js?qmaukd",
|
|
| 58 |
hash:"f2871cc80c52fe8c04c582c4a49797c9c8fd80391cf1452e47f7fe97835ed5cc"}, |
|
| 59 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.mousewheel.min.js?qmaukd",
|
|
| 60 |
hash:"f50233e84c2ac7ada37a094d3f7d3b3f7c97716d6b7b47bf69619d93ee4ac1ce"}, |
|
| 61 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/select2/select2.min.js?qmaukd",
|
|
| 62 |
hash:"ce61298fb9aa4ec49ccd4172d097e36a9e5db3af06a7b82796659368f15b7c1b"}, |
|
| 63 |
{url: "https://my.fsf.org/sites/all/modules/civi crm/packages/jquery/plugins/jquery.form.min.js?qmaukd",
|
|
| 64 |
hash:"c90f0e501d2948fbc2b61bffd654fa4ab64741fd48923782419eeb14d3816fb8"}, |
|
| 65 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.timeentry.min.js?qmaukd",
|
|
| 66 |
hash:"8e85df981e8ad7049d06dfb075277d038734d36a7097c7f021021b2bdccfe9bb"}, |
|
| 67 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.blockUI.min.js?qmaukd",
|
|
| 68 |
hash:"806aedff52ac822f2adc5797073e1e5c5cec32eb9f15f2319cb32a347dcd232b"}, |
|
| 69 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/datatables/media/js/jquery.dataTables.min.js?qmaukd",
|
|
| 70 |
hash:"b796504d9b1b422f0dc6ccc2d740ac78a8c9e5078cc3934836d39742b1121925"}, |
|
| 71 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/jquery-validation/dist/jquery.validate.min.js?qmaukd",
|
|
| 72 |
hash:"f0f5373ad203101ea91bf826c5a7ef8f7cd74887f06bad2cb9277a504503b9e2"}, |
|
| 73 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.ui.datepicker.validation.min.js?qmaukd",
|
|
| 74 |
hash:"c6e6f6bf7f8fff25cca338045774e267e8eaa2d48ac9100540f3d59a6d2b3c61"}, |
|
| 75 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/Common.js?qmaukd",
|
|
| 76 |
hash:"17aa222a3af2e8958be16accb5e77ef39f67009cb3b500718d8fffd45b399148"}, |
|
| 77 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/crm.datepicker.js?qmaukd",
|
|
| 78 |
hash:"9bd8d10208aa99c156325f7819da6f0dd62ba221ac4119c3ccd4834e2cf36535"}, |
|
| 79 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/crm.ajax.js?qmaukd",
|
|
| 80 |
hash:"6401a4e257b7499ae4a00be2c200e4504a2c9b3d6b278a830c31a7b63374f0fe"}, |
|
| 81 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/wysiwyg/crm.wysiwyg.js?qmaukd",
|
|
| 82 |
hash:"fa962356072a36672c3b4b25bdeb657f020995a067e20a29cd5bb84b05157762"}, |
|
| 83 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/noconflict.js?qmaukd",
|
|
| 84 |
hash:"58d6d9f496a235d23cf891926d71f2104e4f2afe1d14bb4e2b5233f646c35e62"}, |
|
| 85 |
{url: "https://my.fsf.org/sites/all/modules/matomo/matomo.js?qmaukd",
|
|
| 86 |
hash:"7f39ccd085f348189cd2fb62ea4d4a658d96f6bba266265880b98605e777e2de"}, |
|
| 87 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/global.js?qmaukd",
|
|
| 88 |
hash:"aa7983f6b902f9f4415cfc8346e0c3f194cc95b78f52f2ad09ec7effa1326b9c"}, |
|
| 89 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/jquery.superfish.min.js?qmaukd",
|
|
| 90 |
hash:"5ef1f93bf3901227056bf9ed0ed93a148eec4dda30f419756b12bedd1098815e"}, |
|
| 91 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/jquery.sidr.min.js?qmaukd",
|
|
| 92 |
hash:"c4914d415826676c6af2e61f16edb72c5388f8600ba6de9049892aee49d980a0"}, |
|
| 93 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/jquery.flexslider.min.js?qmaukd",
|
|
| 94 |
hash:"cefaf715761b4494913851249b9d40dacb4a8cb61242b0efc859dc586d56e0d4"}, |
|
| 95 |
{url: "http://127.0.0.1:8000/myfsf_crap.js",
|
|
| 96 |
hash:"d91ccf21592d0f861ea0ba946bc257fc5d88269327cad0a91387da6cb8ff633e"}, |
|
| 97 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/templates/CRM/Core/BillingBlock.js?r=lp7Di",
|
|
| 98 |
hash:"2f25d35e7a0c0060ab0a444a577f09dd3c9934ae898a7ee0eb20b6c986ab5a1c"}, |
|
| 99 |
{url: "https://my.fsf.org/extensions/com.aghstrategies.giftmemberships/js/giftpricefield.js?r=lp7Di",
|
|
| 100 |
hash:"f86080e6bd306fe46474039aeca2808235005bce5a2a29416d08210022039a45"}, |
|
| 101 |
{url: "https://my.fsf.org/extensions/com.ginkgostreet.negativenegator/js/negativenegator.js?r=lp7Di",
|
|
| 102 |
hash:"d0e87bac832856db70947d82a7ab4e0b7c8b1070d5f1a32335345e033ece3a14"} |
|
| 103 |
]) {
|
|
| 104 |
let name_regex = /\/([^/]+)\.js/; |
|
| 105 |
let name = name_regex.exec(script_data.url)[1]; |
|
| 106 |
await storage.set(TYPE_PREFIX.SCRIPT, name, script_data); |
|
| 107 |
components.push([TYPE_PREFIX.SCRIPT, name]); |
|
| 108 |
} |
|
| 109 |
|
|
| 110 |
await storage.set(TYPE_PREFIX.BAG, "myfsf_join", components); |
|
| 111 |
|
|
| 112 |
await storage.set(TYPE_PREFIX.PAGE, "https://my.fsf.org/join", {
|
|
| 113 |
components: [TYPE_PREFIX.BAG, "myfsf_join"] |
|
| 114 |
}); |
|
| 115 |
|
|
| 116 |
let hello_script = {
|
|
| 117 |
text: "console.log(\"hello, every1!\");\n" |
|
| 118 |
}; |
|
| 119 |
await storage.set(TYPE_PREFIX.SCRIPT, "hello", hello_script); |
|
| 120 |
await storage.set(TYPE_PREFIX.BAG, "hello", |
|
| 121 |
[[TYPE_PREFIX.SCRIPT, "hello"]]); |
|
| 122 |
await storage.set(TYPE_PREFIX.PAGE, "https://my.fsf.org/", {
|
|
| 123 |
components: [TYPE_PREFIX.BAG, "hello"], |
|
| 124 |
allow: true |
|
| 125 |
}); |
|
| 126 |
|
|
| 127 |
let opencores_script = {
|
|
| 128 |
text: `\ |
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT TYPE_PREFIX |
|
| 11 |
* IMPORT get_storage |
|
| 12 |
* IMPORT start_storage_server |
|
| 13 |
* IMPORT start_page_actions_server |
|
| 14 |
* IMPORT start_policy_injector |
|
| 15 |
* IMPORT browser |
|
| 16 |
* IMPORTS_END |
|
| 17 |
*/ |
|
| 18 |
|
|
| 19 |
start_storage_server(); |
|
| 20 |
start_page_actions_server(); |
|
| 21 |
start_policy_injector(); |
|
| 22 |
|
|
| 23 |
async function init_myext(install_details) |
|
| 24 |
{
|
|
| 25 |
console.log("details:", install_details);
|
|
| 26 |
if (install_details.reason != "install") |
|
| 27 |
return; |
|
| 28 |
|
|
| 29 |
let storage = await get_storage(); |
|
| 30 |
|
|
| 31 |
await storage.clear(); |
|
| 32 |
|
|
| 33 |
/* |
|
| 34 |
* Below we add sample settings to the extension. |
|
| 35 |
* Those should be considered example values for viewing in the options |
|
| 36 |
* page. They won't make my.fsf.org work. The only scripts that does |
|
| 37 |
* something useful right now is the opencores one. |
|
| 38 |
*/ |
|
| 39 |
|
|
| 40 |
let components = []; |
|
| 41 |
for (let script_data of [ |
|
| 42 |
{url: "http://127.0.0.1:8000/myfsf_define_CRM.js",
|
|
| 43 |
hash:"bf0cc81c7e8d5f800877b4bc3f14639f946f5ac6d4dc120255ffac5eba5e48fe"}, |
|
| 44 |
{url: "https://my.fsf.org/misc/jquery.js?v=1.4.4",
|
|
| 45 |
hash:"261ae472fa0cbf27c80c9200a1599a60fde581a0e652eee4bf41def8cb61f2d0"}, |
|
| 46 |
{url: "https://my.fsf.org/misc/jquery-extend-3.4.0.js?v=1.4.4",
|
|
| 47 |
hash:"c54103ba57ee210ca55c052e70415402707548a4e6a68dd6efb3895019bee392"}, |
|
| 48 |
{url: "https://my.fsf.org/misc/jquery-html-prefilter-3.5.0-backport.js?v=1.4.4",
|
|
| 49 |
hash:"fad84efa145fb507e5df9b582fa01b1c4e6313de7f72ebdd55726d92fa4dbf06"}, |
|
| 50 |
{url: "https://my.fsf.org/misc/jquery.once.js?v=1.2",
|
|
| 51 |
hash:"1430f42c0d760ba8e05bb3762480502e541f654fec5739ee40625ab22dc38c4f"}, |
|
| 52 |
{url: "https://my.fsf.org/misc/drupal.js?qmaukd",
|
|
| 53 |
hash:"2e08dccbd4d8b728a6871562995a4636b89bfe0ed3b8fb0138191c922228b116"}, |
|
| 54 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/jquery/dist/jquery.min.js?qmaukd",
|
|
| 55 |
hash:"a6d01520d28d15dbe476de84eea90eb3ee2d058722efc062ec73cb5fad78a17b"}, |
|
| 56 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/jquery-ui/jquery-ui.min.js?qmaukd",
|
|
| 57 |
hash:"28ce75d953678c4942df47a11707a15e3c756021cf89090e3e6aa7ad6b6971c3"}, |
|
| 58 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/lodash-compat/lodash.min.js?qmaukd",
|
|
| 59 |
hash:"f2871cc80c52fe8c04c582c4a49797c9c8fd80391cf1452e47f7fe97835ed5cc"}, |
|
| 60 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.mousewheel.min.js?qmaukd",
|
|
| 61 |
hash:"f50233e84c2ac7ada37a094d3f7d3b3f7c97716d6b7b47bf69619d93ee4ac1ce"}, |
|
| 62 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/select2/select2.min.js?qmaukd",
|
|
| 63 |
hash:"ce61298fb9aa4ec49ccd4172d097e36a9e5db3af06a7b82796659368f15b7c1b"}, |
|
| 64 |
{url: "https://my.fsf.org/sites/all/modules/civi crm/packages/jquery/plugins/jquery.form.min.js?qmaukd",
|
|
| 65 |
hash:"c90f0e501d2948fbc2b61bffd654fa4ab64741fd48923782419eeb14d3816fb8"}, |
|
| 66 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.timeentry.min.js?qmaukd",
|
|
| 67 |
hash:"8e85df981e8ad7049d06dfb075277d038734d36a7097c7f021021b2bdccfe9bb"}, |
|
| 68 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.blockUI.min.js?qmaukd",
|
|
| 69 |
hash:"806aedff52ac822f2adc5797073e1e5c5cec32eb9f15f2319cb32a347dcd232b"}, |
|
| 70 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/datatables/media/js/jquery.dataTables.min.js?qmaukd",
|
|
| 71 |
hash:"b796504d9b1b422f0dc6ccc2d740ac78a8c9e5078cc3934836d39742b1121925"}, |
|
| 72 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/bower_components/jquery-validation/dist/jquery.validate.min.js?qmaukd",
|
|
| 73 |
hash:"f0f5373ad203101ea91bf826c5a7ef8f7cd74887f06bad2cb9277a504503b9e2"}, |
|
| 74 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/packages/jquery/plugins/jquery.ui.datepicker.validation.min.js?qmaukd",
|
|
| 75 |
hash:"c6e6f6bf7f8fff25cca338045774e267e8eaa2d48ac9100540f3d59a6d2b3c61"}, |
|
| 76 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/Common.js?qmaukd",
|
|
| 77 |
hash:"17aa222a3af2e8958be16accb5e77ef39f67009cb3b500718d8fffd45b399148"}, |
|
| 78 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/crm.datepicker.js?qmaukd",
|
|
| 79 |
hash:"9bd8d10208aa99c156325f7819da6f0dd62ba221ac4119c3ccd4834e2cf36535"}, |
|
| 80 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/crm.ajax.js?qmaukd",
|
|
| 81 |
hash:"6401a4e257b7499ae4a00be2c200e4504a2c9b3d6b278a830c31a7b63374f0fe"}, |
|
| 82 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/wysiwyg/crm.wysiwyg.js?qmaukd",
|
|
| 83 |
hash:"fa962356072a36672c3b4b25bdeb657f020995a067e20a29cd5bb84b05157762"}, |
|
| 84 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/js/noconflict.js?qmaukd",
|
|
| 85 |
hash:"58d6d9f496a235d23cf891926d71f2104e4f2afe1d14bb4e2b5233f646c35e62"}, |
|
| 86 |
{url: "https://my.fsf.org/sites/all/modules/matomo/matomo.js?qmaukd",
|
|
| 87 |
hash:"7f39ccd085f348189cd2fb62ea4d4a658d96f6bba266265880b98605e777e2de"}, |
|
| 88 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/global.js?qmaukd",
|
|
| 89 |
hash:"aa7983f6b902f9f4415cfc8346e0c3f194cc95b78f52f2ad09ec7effa1326b9c"}, |
|
| 90 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/jquery.superfish.min.js?qmaukd",
|
|
| 91 |
hash:"5ef1f93bf3901227056bf9ed0ed93a148eec4dda30f419756b12bedd1098815e"}, |
|
| 92 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/jquery.sidr.min.js?qmaukd",
|
|
| 93 |
hash:"c4914d415826676c6af2e61f16edb72c5388f8600ba6de9049892aee49d980a0"}, |
|
| 94 |
{url: "https://my.fsf.org/sites/all/themes/fsf_venture/js/jquery.flexslider.min.js?qmaukd",
|
|
| 95 |
hash:"cefaf715761b4494913851249b9d40dacb4a8cb61242b0efc859dc586d56e0d4"}, |
|
| 96 |
{url: "http://127.0.0.1:8000/myfsf_crap.js",
|
|
| 97 |
hash:"d91ccf21592d0f861ea0ba946bc257fc5d88269327cad0a91387da6cb8ff633e"}, |
|
| 98 |
{url: "https://my.fsf.org/sites/all/modules/civicrm/templates/CRM/Core/BillingBlock.js?r=lp7Di",
|
|
| 99 |
hash:"2f25d35e7a0c0060ab0a444a577f09dd3c9934ae898a7ee0eb20b6c986ab5a1c"}, |
|
| 100 |
{url: "https://my.fsf.org/extensions/com.aghstrategies.giftmemberships/js/giftpricefield.js?r=lp7Di",
|
|
| 101 |
hash:"f86080e6bd306fe46474039aeca2808235005bce5a2a29416d08210022039a45"}, |
|
| 102 |
{url: "https://my.fsf.org/extensions/com.ginkgostreet.negativenegator/js/negativenegator.js?r=lp7Di",
|
|
| 103 |
hash:"d0e87bac832856db70947d82a7ab4e0b7c8b1070d5f1a32335345e033ece3a14"} |
|
| 104 |
]) {
|
|
| 105 |
let name_regex = /\/([^/]+)\.js/; |
|
| 106 |
let name = name_regex.exec(script_data.url)[1]; |
|
| 107 |
await storage.set(TYPE_PREFIX.SCRIPT, name, script_data); |
|
| 108 |
components.push([TYPE_PREFIX.SCRIPT, name]); |
|
| 109 |
} |
|
| 110 |
|
|
| 111 |
await storage.set(TYPE_PREFIX.BAG, "myfsf_join", components); |
|
| 112 |
|
|
| 113 |
await storage.set(TYPE_PREFIX.PAGE, "https://my.fsf.org/join", {
|
|
| 114 |
components: [TYPE_PREFIX.BAG, "myfsf_join"] |
|
| 115 |
}); |
|
| 116 |
|
|
| 117 |
let hello_script = {
|
|
| 118 |
text: "console.log(\"hello, every1!\");\n" |
|
| 119 |
}; |
|
| 120 |
await storage.set(TYPE_PREFIX.SCRIPT, "hello", hello_script); |
|
| 121 |
await storage.set(TYPE_PREFIX.BAG, "hello", |
|
| 122 |
[[TYPE_PREFIX.SCRIPT, "hello"]]); |
|
| 123 |
await storage.set(TYPE_PREFIX.PAGE, "https://my.fsf.org/", {
|
|
| 124 |
components: [TYPE_PREFIX.BAG, "hello"], |
|
| 125 |
allow: true |
|
| 126 |
}); |
|
| 127 |
|
|
| 128 |
let opencores_script = {
|
|
| 129 |
text: `\ |
|
| 129 | 130 |
let data = JSON.parse(document.getElementById("__NEXT_DATA__").textContent);
|
| 130 | 131 |
let sections = {};
|
| 131 | 132 |
for (let h1 of document.getElementsByClassName("cMJCrc")) {
|
| ... | ... | |
| 154 | 155 |
ul.appendChild(li); |
| 155 | 156 |
} |
| 156 | 157 |
` |
| 157 |
};
|
|
| 158 |
};
|
|
| 158 | 159 |
|
| 159 |
await storage.set(TYPE_PREFIX.SCRIPT, "opencores", opencores_script);
|
|
| 160 |
await storage.set(TYPE_PREFIX.PAGE, "https://opencores.org/projects", {
|
|
| 161 |
components: [TYPE_PREFIX.SCRIPT, "opencores"],
|
|
| 162 |
allow: false
|
|
| 163 |
});
|
|
| 164 |
}
|
|
| 160 |
await storage.set(TYPE_PREFIX.SCRIPT, "opencores", opencores_script);
|
|
| 161 |
await storage.set(TYPE_PREFIX.PAGE, "https://opencores.org/projects", {
|
|
| 162 |
components: [TYPE_PREFIX.SCRIPT, "opencores"], |
|
| 163 |
allow: false |
|
| 164 |
});
|
|
| 165 |
} |
|
| 165 | 166 |
|
| 166 |
browser.runtime.onInstalled.addListener(init_myext);
|
|
| 167 |
browser.runtime.onInstalled.addListener(init_myext); |
|
| 167 | 168 |
|
| 168 |
console.log("hello, myext");
|
|
| 169 |
})(); |
|
| 169 |
console.log("hello, myext");
|
|
| background/message_server.js | ||
|---|---|---|
| 5 | 5 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 6 |
*/ |
| 7 | 7 |
|
| 8 |
"use strict"; |
|
| 9 |
|
|
| 10 |
(() => {
|
|
| 11 |
const browser = window.browser; |
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT browser |
|
| 11 |
* IMPORTS_END |
|
| 12 |
*/ |
|
| 12 | 13 |
|
| 13 |
var listeners = {};
|
|
| 14 |
var listeners = {};
|
|
| 14 | 15 |
|
| 15 |
/* magic should be one of the constants from /common/connection_types.js */
|
|
| 16 |
/* magic should be one of the constants from /common/connection_types.js */ |
|
| 16 | 17 |
|
| 17 |
function listen_for_connection(magic, cb)
|
|
| 18 |
{
|
|
| 19 |
listeners[magic] = cb;
|
|
| 20 |
}
|
|
| 18 |
function listen_for_connection(magic, cb) |
|
| 19 |
{
|
|
| 20 |
listeners[magic] = cb;
|
|
| 21 |
} |
|
| 21 | 22 |
|
| 22 |
function raw_listen(port) {
|
|
| 23 |
if (listeners[port.name] === undefined) |
|
| 24 |
return; |
|
| 23 |
function raw_listen(port) |
|
| 24 |
{
|
|
| 25 |
if (listeners[port.name] === undefined) |
|
| 26 |
return; |
|
| 25 | 27 |
|
| 26 |
listeners[port.name](port);
|
|
| 27 |
}
|
|
| 28 |
listeners[port.name](port);
|
|
| 29 |
} |
|
| 28 | 30 |
|
| 29 |
browser.runtime.onConnect.addListener(raw_listen);
|
|
| 31 |
browser.runtime.onConnect.addListener(raw_listen); |
|
| 30 | 32 |
|
| 31 |
window.listen_for_connection = listen_for_connection; |
|
| 32 |
})(); |
|
| 33 |
/* |
|
| 34 |
* EXPORTS_START |
|
| 35 |
* EXPORT listen_for_connection |
|
| 36 |
* EXPORTS_END |
|
| 37 |
*/ |
|
| background/page_actions_server.js | ||
|---|---|---|
| 5 | 5 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 6 |
*/ |
| 7 | 7 |
|
| 8 |
"use strict"; |
|
| 9 |
|
|
| 10 |
(() => {
|
|
| 11 |
const get_storage = window.get_storage; |
|
| 12 |
const TYPE_PREFIX = window.TYPE_PREFIX; |
|
| 13 |
const CONNECTION_TYPE = window.CONNECTION_TYPE; |
|
| 14 |
const browser = window.browser; |
|
| 15 |
const listen_for_connection = window.listen_for_connection; |
|
| 16 |
const sha256 = window.sha256; |
|
| 17 |
const get_query_best = window.get_query_best; |
|
| 18 |
|
|
| 19 |
var storage; |
|
| 20 |
var query_best; |
|
| 21 |
var handler; |
|
| 22 |
|
|
| 23 |
function send_scripts(url, port) |
|
| 24 |
{
|
|
| 25 |
let [pattern, settings] = query_best(url); |
|
| 26 |
if (settings === undefined) |
|
| 27 |
return; |
|
| 28 |
|
|
| 29 |
let components = settings.components; |
|
| 30 |
let processed_bags = new Set(); |
|
| 31 |
|
|
| 32 |
if (components !== undefined) |
|
| 33 |
send_scripts_rec([components], port, processed_bags); |
|
| 34 |
} |
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT get_storage |
|
| 11 |
* IMPORT TYPE_PREFIX |
|
| 12 |
* IMPORT CONNECTION_TYPE |
|
| 13 |
* IMPORT browser |
|
| 14 |
* IMPORT listen_for_connection |
|
| 15 |
* IMPORT sha256 |
|
| 16 |
* IMPORT get_query_best |
|
| 17 |
* IMPORTS_END |
|
| 18 |
*/ |
|
| 35 | 19 |
|
| 36 |
// TODO: parallelize script fetching |
|
| 37 |
async function send_scripts_rec(components, port, processed_bags) |
|
| 38 |
{
|
|
| 39 |
for (let [prefix, name] of components) {
|
|
| 40 |
if (prefix === TYPE_PREFIX.BAG) {
|
|
| 41 |
if (processed_bags.has(name)) {
|
|
| 42 |
console.log(`preventing recursive inclusion of bag ${name}`);
|
|
| 43 |
continue; |
|
| 44 |
} |
|
| 45 |
|
|
| 46 |
var bag = storage.get(TYPE_PREFIX.BAG, name); |
|
| 47 |
|
|
| 48 |
if (bag === undefined) {
|
|
| 49 |
console.log(`no bag in storage for key ${name}`);
|
|
| 50 |
continue; |
|
| 51 |
} |
|
| 52 |
|
|
| 53 |
processed_bags.add(name); |
|
| 54 |
await send_scripts_rec(bag, port, processed_bags); |
|
| 55 |
processed_bags.delete(name); |
|
| 56 |
} else {
|
|
| 57 |
let script_text = await get_script_text(name); |
|
| 58 |
if (script_text === undefined) |
|
| 59 |
continue; |
|
| 60 |
|
|
| 61 |
port.postMessage({inject : [script_text]});
|
|
| 20 |
var storage; |
|
| 21 |
var query_best; |
|
| 22 |
var handler; |
|
| 23 |
|
|
| 24 |
function send_scripts(url, port) |
|
| 25 |
{
|
|
| 26 |
let [pattern, settings] = query_best(url); |
|
| 27 |
if (settings === undefined) |
|
| 28 |
return; |
|
| 29 |
|
|
| 30 |
let components = settings.components; |
|
| 31 |
let processed_bags = new Set(); |
|
| 32 |
|
|
| 33 |
if (components !== undefined) |
|
| 34 |
send_scripts_rec([components], port, processed_bags); |
|
| 35 |
} |
|
| 36 |
|
|
| 37 |
// TODO: parallelize script fetching |
|
| 38 |
async function send_scripts_rec(components, port, processed_bags) |
|
| 39 |
{
|
|
| 40 |
for (let [prefix, name] of components) {
|
|
| 41 |
if (prefix === TYPE_PREFIX.BAG) {
|
|
| 42 |
if (processed_bags.has(name)) {
|
|
| 43 |
console.log(`preventing recursive inclusion of bag ${name}`);
|
|
| 44 |
continue; |
|
| 62 | 45 |
} |
| 63 |
} |
|
| 64 |
} |
|
| 65 | 46 |
|
| 66 |
async function get_script_text(script_name) |
|
| 67 |
{
|
|
| 68 |
try {
|
|
| 69 |
let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name); |
|
| 70 |
if (script_data === undefined) {
|
|
| 71 |
console.log(`missing data for ${script_name}`);
|
|
| 72 |
return; |
|
| 47 |
var bag = storage.get(TYPE_PREFIX.BAG, name); |
|
| 48 |
|
|
| 49 |
if (bag === undefined) {
|
|
| 50 |
console.log(`no bag in storage for key ${name}`);
|
|
| 51 |
continue; |
|
| 73 | 52 |
} |
| 74 |
let script_text = script_data.text; |
|
| 75 |
if (!script_text) |
|
| 76 |
script_text = await fetch_remote_script(script_data); |
|
| 77 |
return script_text; |
|
| 78 |
} catch (e) {
|
|
| 79 |
console.log(e); |
|
| 80 |
} |
|
| 81 |
} |
|
| 82 | 53 |
|
| 83 |
function ajax_callback() |
|
| 84 |
{
|
|
| 85 |
if (this.readyState == 4) |
|
| 86 |
this.resolve_callback(this); |
|
| 87 |
} |
|
| 54 |
processed_bags.add(name); |
|
| 55 |
await send_scripts_rec(bag, port, processed_bags); |
|
| 56 |
processed_bags.delete(name); |
|
| 57 |
} else {
|
|
| 58 |
let script_text = await get_script_text(name); |
|
| 59 |
if (script_text === undefined) |
|
| 60 |
continue; |
|
| 88 | 61 |
|
| 89 |
function initiate_ajax_request(resolve, method, url) |
|
| 90 |
{
|
|
| 91 |
var xhttp = new XMLHttpRequest(); |
|
| 92 |
xhttp.resolve_callback = resolve; |
|
| 93 |
xhttp.onreadystatechange = ajax_callback; |
|
| 94 |
xhttp.open(method, url, true); |
|
| 95 |
xhttp.send(); |
|
| 62 |
port.postMessage({inject : [script_text]});
|
|
| 63 |
} |
|
| 96 | 64 |
} |
| 97 |
|
|
| 98 |
function make_ajax_request(method, url) |
|
| 99 |
{
|
|
| 100 |
return new Promise((resolve, reject) => |
|
| 101 |
initiate_ajax_request(resolve, method, url)); |
|
| 65 |
} |
|
| 66 |
|
|
| 67 |
async function get_script_text(script_name) |
|
| 68 |
{
|
|
| 69 |
try {
|
|
| 70 |
let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name); |
|
| 71 |
if (script_data === undefined) {
|
|
| 72 |
console.log(`missing data for ${script_name}`);
|
|
| 73 |
return; |
|
| 74 |
} |
|
| 75 |
let script_text = script_data.text; |
|
| 76 |
if (!script_text) |
|
| 77 |
script_text = await fetch_remote_script(script_data); |
|
| 78 |
return script_text; |
|
| 79 |
} catch (e) {
|
|
| 80 |
console.log(e); |
|
| 102 | 81 |
} |
| 103 |
|
|
| 104 |
async function fetch_remote_script(script_data) |
|
| 105 |
{
|
|
| 106 |
try {
|
|
| 107 |
let xhttp = await make_ajax_request("GET", script_data.url);
|
|
| 108 |
if (xhttp.status === 200) {
|
|
| 109 |
let computed_hash = sha256(xhttp.responseText); |
|
| 110 |
if (computed_hash !== script_data.hash) {
|
|
| 111 |
console.log(`Bad hash for ${script_data.url}\n got ${computed_hash} instead of ${script_data.hash}`);
|
|
| 112 |
return; |
|
| 113 |
} |
|
| 114 |
return xhttp.responseText; |
|
| 115 |
} else {
|
|
| 116 |
console.log("script not fetched: " + script_data.url);
|
|
| 82 |
} |
|
| 83 |
|
|
| 84 |
function ajax_callback() |
|
| 85 |
{
|
|
| 86 |
if (this.readyState == 4) |
|
| 87 |
this.resolve_callback(this); |
|
| 88 |
} |
|
| 89 |
|
|
| 90 |
function initiate_ajax_request(resolve, method, url) |
|
| 91 |
{
|
|
| 92 |
var xhttp = new XMLHttpRequest(); |
|
| 93 |
xhttp.resolve_callback = resolve; |
|
| 94 |
xhttp.onreadystatechange = ajax_callback; |
|
| 95 |
xhttp.open(method, url, true); |
|
| 96 |
xhttp.send(); |
|
| 97 |
} |
|
| 98 |
|
|
| 99 |
function make_ajax_request(method, url) |
|
| 100 |
{
|
|
| 101 |
return new Promise((resolve, reject) => |
|
| 102 |
initiate_ajax_request(resolve, method, url)); |
|
| 103 |
} |
|
| 104 |
|
|
| 105 |
async function fetch_remote_script(script_data) |
|
| 106 |
{
|
|
| 107 |
try {
|
|
| 108 |
let xhttp = await make_ajax_request("GET", script_data.url);
|
|
| 109 |
if (xhttp.status === 200) {
|
|
| 110 |
let computed_hash = sha256(xhttp.responseText); |
|
| 111 |
if (computed_hash !== script_data.hash) {
|
|
| 112 |
console.log(`Bad hash for ${script_data.url}\n got ${computed_hash} instead of ${script_data.hash}`);
|
|
| 117 | 113 |
return; |
| 118 | 114 |
} |
| 119 |
} catch (e) {
|
|
| 120 |
console.log(e); |
|
| 115 |
return xhttp.responseText; |
|
| 116 |
} else {
|
|
| 117 |
console.log("script not fetched: " + script_data.url);
|
|
| 118 |
return; |
|
| 121 | 119 |
} |
| 120 |
} catch (e) {
|
|
| 121 |
console.log(e); |
|
| 122 | 122 |
} |
| 123 |
|
|
| 124 |
function handle_message(port, message, handler) |
|
| 125 |
{
|
|
| 126 |
port.onMessage.removeListener(handler[0]); |
|
| 127 |
let url = message.url; |
|
| 128 |
console.log({url});
|
|
| 129 |
send_scripts(url, port); |
|
| 130 |
} |
|
| 131 |
|
|
| 132 |
function new_connection(port) |
|
| 133 |
{
|
|
| 134 |
console.log("new page actions connection!");
|
|
| 135 |
let handler = []; |
|
| 136 |
handler.push(m => handle_message(port, m, handler)); |
|
| 137 |
port.onMessage.addListener(handler[0]); |
|
| 138 |
} |
|
| 139 |
|
|
| 140 |
async function start() |
|
| 141 |
{
|
|
| 142 |
storage = await get_storage(); |
|
| 143 |
query_best = await get_query_best(); |
|
| 144 |
|
|
| 145 |
listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection); |
|
| 146 |
} |
|
| 147 |
|
|
| 148 |
window.start_page_actions_server = start; |
|
| 149 |
})(); |
|
| 123 |
} |
|
| 124 |
|
|
| 125 |
function handle_message(port, message, handler) |
|
| 126 |
{
|
|
| 127 |
port.onMessage.removeListener(handler[0]); |
|
| 128 |
let url = message.url; |
|
| 129 |
console.log({url});
|
|
| 130 |
send_scripts(url, port); |
|
| 131 |
} |
|
| 132 |
|
|
| 133 |
function new_connection(port) |
|
| 134 |
{
|
|
| 135 |
console.log("new page actions connection!");
|
|
| 136 |
let handler = []; |
|
| 137 |
handler.push(m => handle_message(port, m, handler)); |
|
| 138 |
port.onMessage.addListener(handler[0]); |
|
| 139 |
} |
|
| 140 |
|
|
| 141 |
async function start_page_actions_server() |
|
| 142 |
{
|
|
| 143 |
storage = await get_storage(); |
|
| 144 |
query_best = await get_query_best(); |
|
| 145 |
|
|
| 146 |
listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection); |
|
| 147 |
} |
|
| 148 |
|
|
| 149 |
/* |
|
| 150 |
* EXPORTS_START |
|
| 151 |
* EXPORT start_page_actions_server |
|
| 152 |
* EXPORTS_END |
|
| 153 |
*/ |
|
| background/policy_injector.js | ||
|---|---|---|
| 5 | 5 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 6 |
*/ |
| 7 | 7 |
|
| 8 |
"use strict"; |
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT TYPE_PREFIX |
|
| 11 |
* IMPORT get_storage |
|
| 12 |
* IMPORT browser |
|
| 13 |
* IMPORT is_chrome |
|
| 14 |
* IMPORT gen_unique |
|
| 15 |
* IMPORT url_item |
|
| 16 |
* IMPORT get_query_best |
|
| 17 |
* IMPORTS_END |
|
| 18 |
*/ |
|
| 9 | 19 |
|
| 10 |
(() => {
|
|
| 11 |
const TYPE_PREFIX = window.TYPE_PREFIX; |
|
| 12 |
const get_storage = window.get_storage; |
|
| 13 |
const browser = window.browser; |
|
| 14 |
const is_chrome = window.is_chrome; |
|
| 15 |
const gen_unique = window.gen_unique; |
|
| 16 |
const url_item = window.url_item; |
|
| 17 |
const get_query_best = window.get_query_best; |
|
| 20 |
var storage; |
|
| 21 |
var query_best; |
|
| 18 | 22 |
|
| 19 |
var storage; |
|
| 20 |
var query_best; |
|
| 23 |
let csp_header_names = {
|
|
| 24 |
"content-security-policy" : true, |
|
| 25 |
"x-webkit-csp" : true, |
|
| 26 |
"x-content-security-policy" : true |
|
| 27 |
}; |
|
| 21 | 28 |
|
| 22 |
let csp_header_names = {
|
|
| 23 |
"content-security-policy" : true, |
|
| 24 |
"x-webkit-csp" : true, |
|
| 25 |
"x-content-security-policy" : true |
|
| 26 |
}; |
|
| 29 |
function is_noncsp_header(header) |
|
| 30 |
{
|
|
| 31 |
return !csp_header_names[header.name.toLowerCase()]; |
|
| 32 |
} |
|
| 27 | 33 |
|
| 28 |
function is_noncsp_header(header) |
|
| 29 |
{
|
|
| 30 |
return !csp_header_names[header.name.toLowerCase()]; |
|
| 31 |
} |
|
| 34 |
function inject(details) |
|
| 35 |
{
|
|
| 36 |
let url = url_item(details.url); |
|
| 32 | 37 |
|
| 33 |
function inject(details) |
|
| 34 |
{
|
|
| 35 |
let url = url_item(details.url); |
|
| 38 |
let [pattern, settings] = query_best(url); |
|
| 36 | 39 |
|
| 37 |
let [pattern, settings] = query_best(url); |
|
| 40 |
if (settings !== undefined && settings.allow) |
|
| 41 |
return {cancel : false};
|
|
| 38 | 42 |
|
| 39 |
if (settings !== undefined && settings.allow) {
|
|
| 40 |
console.log("allowing", url);
|
|
| 41 |
return {cancel : false};
|
|
| 42 |
} |
|
| 43 |
let nonce = gen_unique(url).substring(1); |
|
| 44 |
let headers = details.responseHeaders.filter(is_noncsp_header); |
|
| 43 | 45 |
|
| 44 |
let nonce = gen_unique(url).substring(1); |
|
| 45 |
let headers = details.responseHeaders.filter(is_noncsp_header); |
|
| 46 |
headers.push({
|
|
| 47 |
name : "content-security-policy", |
|
| 48 |
value : `script-src 'nonce-${nonce}'; script-src-elem 'nonce-${nonce}';`
|
|
| 49 |
}); |
|
| 46 |
let rule = `script-src 'nonce-${nonce}';`;
|
|
| 47 |
if (is_chrome) |
|
| 48 |
rule += `script-src-elem 'nonce-${nonce}';`;
|
|
| 50 | 49 |
|
| 51 |
console.log("modified headers", url, headers);
|
|
| 50 |
headers.push({
|
|
| 51 |
name : "content-security-policy", |
|
| 52 |
value : rule |
|
| 53 |
}); |
|
| 52 | 54 |
|
| 53 |
return {responseHeaders: headers};
|
|
| 54 |
}
|
|
| 55 |
return {responseHeaders: headers};
|
|
| 56 |
} |
|
| 55 | 57 |
|
| 56 |
async function start() {
|
|
| 57 |
storage = await get_storage(); |
|
| 58 |
query_best = await get_query_best(); |
|
| 58 |
async function start_policy_injector() |
|
| 59 |
{
|
|
| 60 |
storage = await get_storage(); |
|
| 61 |
query_best = await get_query_best(); |
|
| 59 | 62 |
|
| 60 |
let extra_opts = ["blocking", "responseHeaders"];
|
|
| 61 |
if (is_chrome)
|
|
| 62 |
extra_opts.push("extraHeaders");
|
|
| 63 |
let extra_opts = ["blocking", "responseHeaders"];
|
|
| 64 |
if (is_chrome)
|
|
| 65 |
extra_opts.push("extraHeaders");
|
|
| 63 | 66 |
|
| 64 |
browser.webRequest.onHeadersReceived.addListener(
|
|
| 65 |
inject,
|
|
| 66 |
{
|
|
| 67 |
urls: ["<all_urls>"],
|
|
| 68 |
types: ["main_frame", "sub_frame"]
|
|
| 69 |
},
|
|
| 70 |
extra_opts
|
|
| 71 |
);
|
|
| 72 |
}
|
|
| 67 |
browser.webRequest.onHeadersReceived.addListener(
|
|
| 68 |
inject, |
|
| 69 |
{
|
|
| 70 |
urls: ["<all_urls>"],
|
|
| 71 |
types: ["main_frame", "sub_frame"]
|
|
| 72 |
}, |
|
| 73 |
extra_opts |
|
| 74 |
);
|
|
| 75 |
} |
|
| 73 | 76 |
|
| 74 |
window.start_policy_injector = start; |
|
| 75 |
})(); |
|
| 77 |
/* |
|
| 78 |
* EXPORTS_START |
|
| 79 |
* EXPORT start_policy_injector |
|
| 80 |
* EXPORTS_END |
|
| 81 |
*/ |
|
| background/settings_query.js | ||
|---|---|---|
| 5 | 5 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 6 |
*/ |
| 7 | 7 |
|
| 8 |
"use strict"; |
|
| 9 |
|
|
| 10 |
(() => {
|
|
| 11 |
const make_once = window.make_once; |
|
| 12 |
const get_storage = window.get_storage; |
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT make_once |
|
| 11 |
* IMPORT get_storage |
|
| 12 |
* IMPORT TYPE_PREFIX |
|
| 13 |
* IMPORTS_END |
|
| 14 |
*/ |
|
| 13 | 15 |
|
| 14 |
var storage;
|
|
| 16 |
var storage; |
|
| 15 | 17 |
|
| 16 |
var exports = {};
|
|
| 18 |
var exports = {};
|
|
| 17 | 19 |
|
| 18 |
async function init(fun)
|
|
| 19 |
{
|
|
| 20 |
storage = await get_storage();
|
|
| 20 |
async function init(fun) |
|
| 21 |
{
|
|
| 22 |
storage = await get_storage();
|
|
| 21 | 23 |
|
| 22 |
return fun;
|
|
| 23 |
}
|
|
| 24 |
return fun;
|
|
| 25 |
} |
|
| 24 | 26 |
|
| 25 |
// TODO: also support urls with specified ports as well as `data:' urls
|
|
| 26 |
function query(url, multiple)
|
|
| 27 |
{
|
|
| 28 |
let proto_re = "[a-zA-Z]*:\/\/";
|
|
| 29 |
let domain_re = "[^/?#]+";
|
|
| 30 |
let segments_re = "/[^?#]*";
|
|
| 31 |
let query_re = "\\?[^#]*";
|
|
| 27 |
// TODO: also support urls with specified ports
|
|
| 28 |
function query(url, multiple) |
|
| 29 |
{
|
|
| 30 |
let proto_re = "[a-zA-Z]*:\/\/";
|
|
| 31 |
let domain_re = "[^/?#]+";
|
|
| 32 |
let segments_re = "/[^?#]*";
|
|
| 33 |
let query_re = "\\?[^#]*";
|
|
| 32 | 34 |
|
| 33 |
let url_regex = new RegExp(`\
|
|
| 35 |
let url_regex = new RegExp(`\
|
|
| 34 | 36 |
^\ |
| 35 | 37 |
(${proto_re})\
|
| 36 | 38 |
(${domain_re})\
|
| ... | ... | |
| 39 | 41 |
#?.*\$\ |
| 40 | 42 |
`); |
| 41 | 43 |
|
| 42 |
let regex_match = url_regex.exec(url); |
|
| 43 |
if (regex_match === null) {
|
|
| 44 |
console.log("bad url format", url);
|
|
| 45 |
return multiple ? [] : [undefined, undefined]; |
|
| 46 |
} |
|
| 44 |
let regex_match = url_regex.exec(url); |
|
| 45 |
if (regex_match === null) {
|
|
| 46 |
console.log("bad url format", url);
|
|
| 47 |
return multiple ? [] : [undefined, undefined]; |
|
| 48 |
} |
|
| 49 |
|
|
| 50 |
let [_, proto, domain, segments, query] = regex_match; |
|
| 51 |
|
|
| 52 |
domain = domain.split(".");
|
|
| 53 |
let segments_trailing_dash = |
|
| 54 |
segments && segments[segments.length - 1] === "/"; |
|
| 55 |
segments = (segments || "").split("/").filter(s => s !== "");
|
|
| 56 |
segments.unshift("");
|
|
| 57 |
|
|
| 58 |
let matched = []; |
|
| 59 |
|
|
| 60 |
for (let d_slice = 0; d_slice < domain.length; d_slice++) {
|
|
| 61 |
let domain_part = domain.slice(d_slice).join(".");
|
|
| 62 |
let domain_wildcards = []; |
|
| 63 |
if (d_slice === 0) |
|
| 64 |
domain_wildcards.push("");
|
|
| 65 |
if (d_slice === 1) |
|
| 66 |
domain_wildcards.push("*.");
|
|
| 67 |
if (d_slice > 0) |
|
| 68 |
domain_wildcards.push("**.");
|
|
| 69 |
domain_wildcards.push("***.");
|
|
| 70 |
|
|
| 71 |
for (let domain_wildcard of domain_wildcards) {
|
|
| 72 |
let domain_pattern = domain_wildcard + domain_part; |
|
| 73 |
|
|
| 74 |
for (let s_slice = segments.length; s_slice > 0; s_slice--) {
|
|
| 75 |
let segments_part = segments.slice(0, s_slice).join("/");
|
|
| 76 |
let segments_wildcards = []; |
|
| 77 |
if (s_slice === segments.length) {
|
|
| 78 |
if (segments_trailing_dash) |
|
| 79 |
segments_wildcards.push("/");
|
|
| 80 |
segments_wildcards.push("");
|
|
| 81 |
} |
|
| 82 |
if (s_slice === segments.length - 1) {
|
|
| 83 |
if (segments[s_slice] !== "*") |
|
| 84 |
segments_wildcards.push("/*");
|
|
| 85 |
} |
|
| 86 |
if (s_slice < segments.length && |
|
| 87 |
(segments[s_slice] !== "**" || |
|
| 88 |
s_slice < segments.length - 1)) |
|
| 89 |
segments_wildcards.push("/**");
|
|
| 90 |
if (segments[s_slice] !== "***" || |
|
| 91 |
s_slice < segments.length) |
|
| 92 |
segments_wildcards.push("/***");
|
|
| 93 |
|
|
| 94 |
for (let segments_wildcard of segments_wildcards) {
|
|
| 95 |
let segments_pattern = |
|
| 96 |
segments_part + segments_wildcard; |
|
| 47 | 97 |
|
| 48 |
let [_, proto, domain, segments, query] = regex_match; |
|
| 49 |
|
|
| 50 |
domain = domain.split(".");
|
|
| 51 |
let segments_trailing_dash = |
|
| 52 |
segments && segments[segments.length - 1] === "/"; |
|
| 53 |
segments = (segments || "").split("/").filter(s => s !== "");
|
|
| 54 |
segments.unshift("");
|
|
| 55 |
|
|
| 56 |
let matched = []; |
|
| 57 |
|
|
| 58 |
for (let d_slice = 0; d_slice < domain.length; d_slice++) {
|
|
| 59 |
let domain_part = domain.slice(d_slice).join(".");
|
|
| 60 |
let domain_wildcards = []; |
|
| 61 |
if (d_slice === 0) |
|
| 62 |
domain_wildcards.push("");
|
|
| 63 |
if (d_slice === 1) |
|
| 64 |
domain_wildcards.push("*.");
|
|
| 65 |
if (d_slice > 0) |
|
| 66 |
domain_wildcards.push("**.");
|
|
| 67 |
domain_wildcards.push("***.");
|
|
| 68 |
|
|
| 69 |
for (let domain_wildcard of domain_wildcards) {
|
|
| 70 |
let domain_pattern = domain_wildcard + domain_part; |
|
| 71 |
|
|
| 72 |
for (let s_slice = segments.length; s_slice > 0; s_slice--) {
|
|
| 73 |
let segments_part = segments.slice(0, s_slice).join("/");
|
|
| 74 |
let segments_wildcards = []; |
|
| 75 |
if (s_slice === segments.length) {
|
|
| 76 |
if (segments_trailing_dash) |
|
| 77 |
segments_wildcards.push("/");
|
|
| 78 |
segments_wildcards.push("");
|
|
| 79 |
} |
|
| 80 |
if (s_slice === segments.length - 1) {
|
|
| 81 |
if (segments[s_slice] !== "*") |
|
| 82 |
segments_wildcards.push("/*");
|
|
| 83 |
} |
|
| 84 |
if (s_slice < segments.length && |
|
| 85 |
(segments[s_slice] !== "**" || |
|
| 86 |
s_slice < segments.length - 1)) |
|
| 87 |
segments_wildcards.push("/**");
|
|
| 88 |
if (segments[s_slice] !== "***" || |
|
| 89 |
s_slice < segments.length) |
|
| 90 |
segments_wildcards.push("/***");
|
|
| 91 |
|
|
| 92 |
for (let segments_wildcard of segments_wildcards) {
|
|
| 93 |
let segments_pattern = |
|
| 94 |
segments_part + segments_wildcard; |
|
| 95 |
|
|
| 96 |
let pattern = proto + domain_pattern + segments_pattern; |
|
| 97 |
console.log("trying", pattern);
|
|
| 98 |
let settings = storage.get(TYPE_PREFIX.PAGE, pattern); |
|
| 99 |
|
|
| 100 |
if (settings === undefined) |
|
| 101 |
continue; |
|
| 102 |
|
|
| 103 |
if (!multiple) |
|
| 104 |
return [pattern, settings]; |
|
| 105 |
|
|
| 106 |
matched.push([pattern, settings]); |
|
| 107 |
} |
|
| 98 |
let pattern = proto + domain_pattern + segments_pattern; |
|
| 99 |
console.log("trying", pattern);
|
|
| 100 |
let settings = storage.get(TYPE_PREFIX.PAGE, pattern); |
|
| 101 |
|
|
| 102 |
if (settings === undefined) |
|
| 103 |
continue; |
|
| 104 |
|
|
| 105 |
if (!multiple) |
|
| 106 |
return [pattern, settings]; |
|
| 107 |
|
|
| 108 |
matched.push([pattern, settings]); |
|
| 108 | 109 |
} |
| 109 | 110 |
} |
| 110 | 111 |
} |
| 111 |
|
|
| 112 |
return multiple ? matched : [undefined, undefined]; |
|
| 113 | 112 |
} |
| 114 | 113 |
|
| 115 |
function query_best(url) |
|
| 116 |
{
|
|
| 117 |
return query(url, false); |
|
| 118 |
} |
|
| 114 |
return multiple ? matched : [undefined, undefined]; |
|
| 115 |
} |
|
| 119 | 116 |
|
| 120 |
function query_all(url) |
|
| 121 |
{
|
|
| 122 |
return query(url, true); |
|
| 123 |
} |
|
| 117 |
function query_best(url) |
|
| 118 |
{
|
|
| 119 |
return query(url, false); |
|
| 120 |
} |
|
| 121 |
|
|
| 122 |
function query_all(url) |
|
| 123 |
{
|
|
| 124 |
return query(url, true); |
|
| 125 |
} |
|
| 124 | 126 |
|
| 125 |
window.get_query_best = make_once(() => init(query_best)); |
|
| 126 |
window.get_query_all = make_once(() => init(query_all)); |
|
| 127 |
})(); |
|
| 127 |
const get_query_best = make_once(() => init(query_best)); |
|
| 128 |
const get_query_all = make_once(() => init(query_all)); |
|
| 129 |
|
|
| 130 |
/* |
|
| 131 |
* EXPORTS_START |
|
| 132 |
* EXPORT get_query_best |
|
| 133 |
* EXPORT get_query_all |
|
| 134 |
* EXPORTS_END |
|
| 135 |
*/ |
|
| background/storage.js | ||
|---|---|---|
| 5 | 5 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 6 |
*/ |
| 7 | 7 |
|
| 8 |
"use strict"; |
|
| 9 |
|
|
| 10 |
(() => {
|
|
| 11 |
const TYPE_PREFIX = window.TYPE_PREFIX; |
|
| 12 |
const TYPE_NAME = window.TYPE_NAME; |
|
| 13 |
const list_prefixes = window.list_prefixes; |
|
| 14 |
const make_lock = window.make_lock; |
|
| 15 |
const lock = window.lock; |
|
| 16 |
const unlock = window.unlock; |
|
| 17 |
const make_once = window.make_once; |
|
| 18 |
const browser = window.browser; |
|
| 19 |
const is_chrome = window.is_chrome; |
|
| 20 |
|
|
| 21 |
var exports = {};
|
|
| 22 |
|
|
| 23 |
/* We're yet to decide how to handle errors... */ |
|
| 24 |
|
|
| 25 |
/* Here are some basic wrappers for storage API functions */ |
|
| 26 |
|
|
| 27 |
async function get(key) |
|
| 28 |
{
|
|
| 29 |
try {
|
|
| 30 |
/* Fix for fact that Chrome does not use promises here */ |
|
| 31 |
let promise = is_chrome ? |
|
| 32 |
new Promise((resolve, reject) => |
|
| 33 |
chrome.storage.local.get(key, |
|
| 34 |
val => resolve(val))) : |
|
| 35 |
browser.storage.local.get(key); |
|
| 36 |
|
|
| 37 |
return (await promise)[key]; |
|
| 38 |
} catch (e) {
|
|
| 39 |
console.log(e); |
|
| 40 |
} |
|
| 41 |
} |
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT TYPE_PREFIX |
|
| 11 |
* IMPORT TYPE_NAME |
|
| 12 |
* IMPORT list_prefixes |
|
| 13 |
* IMPORT make_lock |
|
| 14 |
* IMPORT lock |
|
| 15 |
* IMPORT unlock |
|
| 16 |
* IMPORT make_once |
|
| 17 |
* IMPORT browser |
|
| 18 |
* IMPORT is_chrome |
|
| 19 |
* IMPORTS_END |
|
| 20 |
*/ |
|
| 42 | 21 |
|
| 43 |
async function set(key, value) |
|
| 44 |
{
|
|
| 45 |
try {
|
|
| 46 |
return browser.storage.local.set({[key]: value});
|
|
| 47 |
} catch (e) {
|
|
| 48 |
console.log(e); |
|
| 49 |
} |
|
| 50 |
} |
|
| 22 |
var exports = {};
|
|
| 51 | 23 |
|
| 52 |
async function setn(keys_and_values) |
|
| 53 |
{
|
|
| 54 |
let obj = Object(); |
|
| 55 |
while (keys_and_values.length > 1) {
|
|
| 56 |
let value = keys_and_values.pop(); |
|
| 57 |
let key = keys_and_values.pop(); |
|
| 58 |
obj[key] = value; |
|
| 59 |
} |
|
| 24 |
/* We're yet to decide how to handle errors... */ |
|
| 60 | 25 |
|
| 61 |
try {
|
|
| 62 |
return browser.storage.local.set(obj); |
|
| 63 |
} catch (e) {
|
|
| 64 |
console.log(e); |
|
| 65 |
} |
|
| 66 |
} |
|
| 26 |
/* Here are some basic wrappers for storage API functions */ |
|
| 67 | 27 |
|
| 68 |
async function set_var(name, value) |
|
| 69 |
{
|
|
| 70 |
return set(TYPE_PREFIX.VAR + name, value); |
|
| 71 |
} |
|
| 28 |
async function get(key) |
|
| 29 |
{
|
|
| 30 |
try {
|
|
| 31 |
/* Fix for fact that Chrome does not use promises here */ |
|
| 32 |
let promise = is_chrome ? |
|
| 33 |
new Promise((resolve, reject) => |
|
| 34 |
chrome.storage.local.get(key, |
|
| 35 |
val => resolve(val))) : |
|
| 36 |
browser.storage.local.get(key); |
|
| 72 | 37 |
|
| 73 |
async function get_var(name) |
|
| 74 |
{
|
|
| 75 |
return get(TYPE_PREFIX.VAR + name); |
|
| 38 |
return (await promise)[key]; |
|
| 39 |
} catch (e) {
|
|
| 40 |
console.log(e); |
|
| 41 |
} |
|
| 42 |
} |
|
| 43 |
|
|
| 44 |
async function set(key, value) |
|
| 45 |
{
|
|
| 46 |
try {
|
|
| 47 |
return browser.storage.local.set({[key]: value});
|
|
| 48 |
} catch (e) {
|
|
| 49 |
console.log(e); |
|
| 50 |
} |
|
| 51 |
} |
|
| 52 |
|
|
| 53 |
async function setn(keys_and_values) |
|
| 54 |
{
|
|
| 55 |
let obj = Object(); |
|
| 56 |
while (keys_and_values.length > 1) {
|
|
| 57 |
let value = keys_and_values.pop(); |
|
| 58 |
let key = keys_and_values.pop(); |
|
| 59 |
obj[key] = value; |
|
| 76 | 60 |
} |
| 77 | 61 |
|
| 78 |
/* |
|
| 79 |
* A special case of persisted variable is one that contains list |
|
| 80 |
* of items. |
|
| 81 |
*/ |
|
| 82 |
|
|
| 83 |
async function get_list_var(name) |
|
| 84 |
{
|
|
| 85 |
let list = await get_var(name); |
|
| 86 |
|
|
| 87 |
return list === undefined ? [] : list; |
|
| 62 |
try {
|
|
| 63 |
return browser.storage.local.set(obj); |
|
| 64 |
} catch (e) {
|
|
| 65 |
console.log(e); |
|
| 88 | 66 |
} |
| 67 |
} |
|
| 89 | 68 |
|
| 90 |
/* We maintain in-memory copies of some stored lists. */ |
|
| 69 |
async function set_var(name, value) |
|
| 70 |
{
|
|
| 71 |
return set(TYPE_PREFIX.VAR + name, value); |
|
| 72 |
} |
|
| 91 | 73 |
|
| 92 |
async function list(prefix)
|
|
| 93 |
{
|
|
| 94 |
let name = TYPE_NAME[prefix] + "s"; /* Make plural. */
|
|
| 95 |
let map = new Map();
|
|
| 74 |
async function get_var(name)
|
|
| 75 |
{
|
|
| 76 |
return get(TYPE_PREFIX.VAR + name);
|
|
| 77 |
}
|
|
| 96 | 78 |
|
| 97 |
for (let item of await get_list_var(name)) |
|
| 98 |
map.set(item, await get(prefix + item)); |
|
| 79 |
/* |
|
| 80 |
* A special case of persisted variable is one that contains list |
|
| 81 |
* of items. |
|
| 82 |
*/ |
|
| 99 | 83 |
|
| 100 |
return {map, prefix, name, listeners : new Set(), lock : make_lock()};
|
|
| 101 |
} |
|
| 84 |
async function get_list_var(name) |
|
| 85 |
{
|
|
| 86 |
let list = await get_var(name); |
|
| 102 | 87 |
|
| 103 |
var pages; |
|
| 104 |
var bags; |
|
| 105 |
var scripts; |
|
| 88 |
return list === undefined ? [] : list; |
|
| 89 |
} |
|
| 106 | 90 |
|
| 107 |
var list_by_prefix = {};
|
|
| 91 |
/* We maintain in-memory copies of some stored lists. */
|
|
| 108 | 92 |
|
| 109 |
async function init()
|
|
| 110 |
{
|
|
| 111 |
for (let prefix of list_prefixes)
|
|
| 112 |
list_by_prefix[prefix] = await list(prefix);
|
|
| 93 |
async function list(prefix)
|
|
| 94 |
{
|
|
| 95 |
let name = TYPE_NAME[prefix] + "s"; /* Make plural. */
|
|
| 96 |
let map = new Map();
|
|
| 113 | 97 |
|
| 114 |
return exports;
|
|
| 115 |
}
|
|
| 98 |
for (let item of await get_list_var(name))
|
|
| 99 |
map.set(item, await get(prefix + item));
|
|
| 116 | 100 |
|
| 117 |
/* |
|
| 118 |
* Facilitate listening to changes |
|
| 119 |
*/ |
|
| 101 |
return {map, prefix, name, listeners : new Set(), lock : make_lock()};
|
|
| 102 |
} |
|
| 120 | 103 |
|
| 121 |
exports.add_change_listener = function (cb, prefixes=list_prefixes) |
|
| 122 |
{
|
|
| 123 |
if (typeof(prefixes) === "string") |
|
| 124 |
prefixes = [prefixes]; |
|
| 104 |
var pages; |
|
| 105 |
var bags; |
|
| 106 |
var scripts; |
|
| 125 | 107 |
|
| 126 |
for (let prefix of prefixes) |
|
| 127 |
list_by_prefix[prefix].listeners.add(cb); |
|
| 128 |
} |
|
| 108 |
var list_by_prefix = {};
|
|
| 129 | 109 |
|
| 130 |
exports.remove_change_listener = function (cb, prefixes=list_prefixes)
|
|
| 131 |
{
|
|
| 132 |
if (typeof(prefixes) === "string")
|
|
| 133 |
prefixes = [prefixes];
|
|
| 110 |
async function init()
|
|
| 111 |
{
|
|
| 112 |
for (let prefix of list_prefixes)
|
|
| 113 |
list_by_prefix[prefix] = await list(prefix);
|
|
| 134 | 114 |
|
| 135 |
for (let prefix of prefixes) |
|
| 136 |
list_by_prefix[prefix].listeners.delete(cb); |
|
| 137 |
} |
|
| 115 |
return exports; |
|
| 116 |
} |
|
| 138 | 117 |
|
| 139 |
function broadcast_change(change, list) |
|
| 140 |
{
|
|
| 141 |
for (let listener_callback of list.listeners) |
|
| 142 |
listener_callback(change); |
|
| 143 |
} |
|
| 118 |
/* |
|
| 119 |
* Facilitate listening to changes |
|
| 120 |
*/ |
|
| 144 | 121 |
|
| 145 |
/* Prepare some hepler functions to get elements of a list */ |
|
| 122 |
exports.add_change_listener = function (cb, prefixes=list_prefixes) |
|
| 123 |
{
|
|
| 124 |
if (typeof(prefixes) === "string") |
|
| 125 |
prefixes = [prefixes]; |
|
| 146 | 126 |
|
| 147 |
function list_items_it(list, with_values=false) |
|
| 148 |
{
|
|
| 149 |
return with_values ? list.map.entries() : list.map.keys(); |
|
| 150 |
} |
|
| 127 |
for (let prefix of prefixes) |
|
| 128 |
list_by_prefix[prefix].listeners.add(cb); |
|
| 129 |
} |
|
| 151 | 130 |
|
| 152 |
function list_entries_it(list)
|
|
| 153 |
{
|
|
| 154 |
return list_items_it(list, true);
|
|
| 155 |
}
|
|
| 131 |
exports.remove_change_listener = function (cb, prefixes=list_prefixes)
|
|
| 132 |
{
|
|
| 133 |
if (typeof(prefixes) === "string")
|
|
| 134 |
prefixes = [prefixes];
|
|
| 156 | 135 |
|
| 157 |
function list_items(list, with_values=false)
|
|
| 158 |
{
|
|
| 159 |
let array = [];
|
|
| 136 |
for (let prefix of prefixes)
|
|
| 137 |
list_by_prefix[prefix].listeners.delete(cb);
|
|
| 138 |
}
|
|
| 160 | 139 |
|
| 161 |
for (let item of list_items_it(list, with_values)) |
|
| 162 |
array.push(item); |
|
| 140 |
function broadcast_change(change, list) |
|
| 141 |
{
|
|
| 142 |
for (let listener_callback of list.listeners) |
|
| 143 |
listener_callback(change); |
|
| 144 |
} |
|
| 163 | 145 |
|
| 164 |
return array; |
|
| 165 |
} |
|
| 146 |
/* Prepare some hepler functions to get elements of a list */ |
|
| 166 | 147 |
|
| 167 |
function list_entries(list)
|
|
| 168 |
{
|
|
| 169 |
return list_items(list, true);
|
|
| 170 |
}
|
|
| 148 |
function list_items_it(list, with_values=false)
|
|
| 149 |
{
|
|
| 150 |
return with_values ? list.map.entries() : list.map.keys();
|
|
| 151 |
} |
|
| 171 | 152 |
|
| 172 |
/* |
|
| 173 |
* Below we make additional effort to update map of given kind of items |
|
| 174 |
* every time an item is added/removed to keep everything coherent. |
|
| 175 |
*/ |
|
| 176 |
async function set_item(item, value, list) |
|
| 177 |
{
|
|
| 178 |
await lock(list.lock); |
|
| 179 |
let result = await _set_item(...arguments); |
|
| 180 |
unlock(list.lock) |
|
| 181 |
return result; |
|
| 182 |
} |
|
| 183 |
async function _set_item(item, value, list) |
|
| 184 |
{
|
|
| 185 |
let key = list.prefix + item; |
|
| 186 |
let old_val = list.map.get(item); |
|
| 187 |
if (old_val === undefined) {
|
|
| 188 |
let items = list_items(list); |
|
| 189 |
items.push(item); |
|
| 190 |
await setn([key, value, "_" + list.name, items]); |
|
| 191 |
} else {
|
|
| 192 |
await set(key, value); |
|
| 193 |
} |
|
| 153 |
function list_entries_it(list) |
|
| 154 |
{
|
|
| 155 |
return list_items_it(list, true); |
|
| 156 |
} |
|
| 194 | 157 |
|
| 195 |
list.map.set(item, value) |
|
| 158 |
function list_items(list, with_values=false) |
|
| 159 |
{
|
|
| 160 |
let array = []; |
|
| 196 | 161 |
|
| 197 |
let change = {
|
|
| 198 |
prefix : list.prefix, |
|
| 199 |
item, |
|
| 200 |
old_val, |
|
| 201 |
new_val : value |
|
| 202 |
}; |
|
| 162 |
for (let item of list_items_it(list, with_values)) |
|
| 163 |
array.push(item); |
|
| 203 | 164 |
|
| 204 |
broadcast_change(change, list); |
|
| 165 |
return array; |
|
| 166 |
} |
|
| 205 | 167 |
|
| 206 |
return old_val; |
|
| 207 |
} |
|
| 168 |
function list_entries(list) |
|
| 169 |
{
|
|
| 170 |
return list_items(list, true); |
|
| 171 |
} |
|
| 208 | 172 |
|
| 209 |
// TODO: The actual idea to set value to undefined is good - this way we can |
|
| 210 |
// also set a new list of items in the same API call. But such key |
|
| 211 |
// is still stored in the storage. We need to somehow remove it later. |
|
| 212 |
// For that, we're going to have to store 1 more list of each kind. |
|
| 213 |
async function remove_item(item, list) |
|
| 214 |
{
|
|
| 215 |
await lock(list.lock); |
|
| 216 |
let result = await _remove_item(...arguments); |
|
| 217 |
unlock(list.lock) |
|
| 218 |
return result; |
|
| 173 |
/* |
|
| 174 |
* Below we make additional effort to update map of given kind of items |
|
| 175 |
* every time an item is added/removed to keep everything coherent. |
|
| 176 |
*/ |
|
| 177 |
async function set_item(item, value, list) |
|
| 178 |
{
|
|
| 179 |
await lock(list.lock); |
|
| 180 |
let result = await _set_item(...arguments); |
|
| 181 |
unlock(list.lock) |
|
| 182 |
return result; |
|
| 183 |
} |
|
| 184 |
async function _set_item(item, value, list) |
|
| 185 |
{
|
|
| 186 |
let key = list.prefix + item; |
|
| 187 |
let old_val = list.map.get(item); |
|
| 188 |
if (old_val === undefined) {
|
|
| 189 |
let items = list_items(list); |
|
| 190 |
items.push(item); |
|
| 191 |
await setn([key, value, "_" + list.name, items]); |
|
| 192 |
} else {
|
|
| 193 |
await set(key, value); |
|
| 219 | 194 |
} |
| 220 |
async function _remove_item(item, list) |
|
| 221 |
{
|
|
| 222 |
let old_val = list.map.get(item); |
|
| 195 |
|
|
| 196 |
list.map.set(item, value) |
|
| 197 |
|
|
| 198 |
let change = {
|
|
| 199 |
prefix : list.prefix, |
|
| 200 |
item, |
|
| 201 |
old_val, |
|
| 202 |
new_val : value |
|
| 203 |
}; |
|
| 204 |
|
|
| 205 |
broadcast_change(change, list); |
|
| 206 |
|
|
| 207 |
return old_val; |
|
| 208 |
} |
|
| 209 |
|
|
| 210 |
// TODO: The actual idea to set value to undefined is good - this way we can |
|
| 211 |
// also set a new list of items in the same API call. But such key |
|
| 212 |
// is still stored in the storage. We need to somehow remove it later. |
|
| 213 |
// For that, we're going to have to store 1 more list of each kind. |
|
| 214 |
async function remove_item(item, list) |
|
| 215 |
{
|
|
| 216 |
await lock(list.lock); |
|
| 217 |
let result = await _remove_item(...arguments); |
|
| 218 |
unlock(list.lock) |
|
| 219 |
return result; |
|
| 220 |
} |
|
| 221 |
async function _remove_item(item, list) |
|
| 222 |
{
|
|
| 223 |
let old_val = list.map.get(item); |
|
| 224 |
if (old_val === undefined) |
|
| 225 |
return; |
|
| 226 |
|
|
| 227 |
let key = list.prefix + item; |
|
| 228 |
let items = list_items(list); |
|
| 229 |
let index = items.indexOf(item); |
|
| 230 |
items.splice(index, 1); |
|
| 231 |
|
|
| 232 |
await setn([key, undefined, "_" + list.name, items]); |
|
| 233 |
|
|
| 234 |
list.map.delete(item); |
|
| 235 |
|
|
| 236 |
let change = {
|
|
| 237 |
prefix : list.prefix, |
|
| 238 |
item, |
|
| 239 |
old_val, |
|
| 240 |
new_val : undefined |
|
| 241 |
}; |
|
| 242 |
|
|
| 243 |
broadcast_change(change, list); |
|
| 244 |
|
|
| 245 |
return old_val; |
|
| 246 |
} |
|
| 247 |
|
|
| 248 |
// TODO: same as above applies here |
|
| 249 |
async function replace_item(old_item, new_item, list, new_val=undefined) |
|
Also available in: Unified diff
emply an sh-based build system; make some changes to blocking