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