Project

General

Profile

Download (4.06 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / common / storage_client.js @ 261548ff

1
/**
2
 * Myext storage through connection (client side)
3
 *
4
 * Copyright (C) 2021 Wojtek Kosior
5
 * Redistribution terms are gathered in the `copyright' file.
6
 */
7

    
8
/*
9
 * IMPORTS_START
10
 * IMPORT CONNECTION_TYPE
11
 * IMPORT TYPE_PREFIX
12
 * IMPORT list_prefixes
13
 * IMPORT make_once
14
 * IMPORT browser
15
 * IMPORTS_END
16
 */
17

    
18
var call_id = 0;
19
var port;
20
var calls_waiting = new Map();
21

    
22
function set_call_callback(resolve, reject, func, args)
23
{
24
    port.postMessage([call_id, func, args]);
25
    calls_waiting.set(call_id++, [resolve, reject]);
26
}
27

    
28
async function remote_call(func, args)
29
{
30
    return new Promise((resolve, reject) =>
31
		       set_call_callback(resolve, reject, func, args));
32
}
33

    
34
function handle_message(message)
35
{
36
    let callbacks = calls_waiting.get(message.call_id);
37
    if (callbacks === undefined) {
38
	handle_change(message);
39
	return;
40
    }
41

    
42
    let [resolve, reject] = callbacks;
43
    calls_waiting.delete(message.call_id);
44
    if (message.error !== undefined)
45
	setTimeout(reject, 0, message.error);
46
    else
47
	setTimeout(resolve, 0, message.result);
48
}
49

    
50
function list(name, prefix)
51
{
52
    return {prefix, name, listeners : new Set()};
53
}
54

    
55
var scripts = list("scripts", TYPE_PREFIX.SCRIPT);
56
var bags = list("bags", TYPE_PREFIX.BAG);
57
var pages = list("pages", TYPE_PREFIX.PAGE);
58

    
59
const list_by_prefix = {
60
    [TYPE_PREFIX.SCRIPT] : scripts,
61
    [TYPE_PREFIX.BAG] : bags,
62
    [TYPE_PREFIX.PAGE] : pages
63
};
64

    
65
var resolve_init;
66

    
67
function handle_first_message(message)
68
{
69
    for (let prefix of Object.keys(message))
70
	list_by_prefix[prefix].map = new Map(message[prefix]);
71

    
72
    port.onMessage.removeListener(handle_first_message);
73
    port.onMessage.addListener(handle_message);
74

    
75
    resolve_init();
76
}
77

    
78
function handle_change(change)
79
{
80
    let list = list_by_prefix[change.prefix];
81

    
82
    if (change.new_val === undefined)
83
	list.map.delete(change.item);
84
    else
85
	list.map.set(change.item, change.new_val);
86

    
87
    for (let listener_callback of list.listeners)
88
	listener_callback(change);
89
}
90

    
91
var exports = {};
92

    
93
function start_connection(resolve)
94
{
95
    resolve_init = resolve;
96
    port = browser.runtime.connect({name : CONNECTION_TYPE.REMOTE_STORAGE});
97
    port.onMessage.addListener(handle_first_message);
98
}
99

    
100
async function init() {
101
    await new Promise((resolve, reject) => start_connection(resolve));
102
    return exports;
103
}
104

    
105
for (let call_name of ["set", "remove", "replace", "clear"])
106
    exports [call_name] = (...args) => remote_call(call_name, args);
107

    
108
// TODO: Much of the code below is copy-pasted from /background/storage.mjs.
109
//       This should later be refactored into a separate module
110
//       to avoid duplication.
111

    
112
/*
113
 * Facilitate listening to changes
114
 */
115

    
116
exports.add_change_listener = function (cb, prefixes=list_prefixes)
117
{
118
    if (typeof(prefixes) === "string")
119
	prefixes = [prefixes];
120

    
121
    for (let prefix of prefixes)
122
	list_by_prefix[prefix].listeners.add(cb);
123
}
124

    
125
exports.remove_change_listener = function (cb, prefixes=list_prefixes)
126
{
127
    if (typeof(prefixes) === "string")
128
	prefixes = [prefixes];
129

    
130
    for (let prefix of prefixes)
131
	list_by_prefix[prefix].listeners.delete(cb);
132
}
133

    
134
/* Prepare some hepler functions to get elements of a list */
135

    
136
function list_items_it(list, with_values=false)
137
{
138
    return with_values ? list.map.entries() : list.map.keys();
139
}
140

    
141
function list_entries_it(list)
142
{
143
    return list_items_it(list, true);
144
}
145

    
146
function list_items(list, with_values=false)
147
{
148
    let array = [];
149

    
150
    for (let item of list_items_it(list, with_values))
151
	array.push(item);
152

    
153
    return array;
154
}
155

    
156
function list_entries(list)
157
{
158
    return list_items(list, true);
159
}
160

    
161
exports.get = function (prefix, item)
162
{
163
    return list_by_prefix[prefix].map.get(item);
164
}
165

    
166
exports.get_all_names = function (prefix)
167
{
168
    return list_items(list_by_prefix[prefix]);
169
}
170

    
171
exports.get_all_names_it = function (prefix)
172
{
173
    return list_items_it(list_by_prefix[prefix]);
174
}
175

    
176
exports.get_all = function (prefix)
177
{
178
    return list_entries(list_by_prefix[prefix]);
179
}
180

    
181
exports.get_all_it = function (prefix)
182
{
183
    return list_entries_it(list_by_prefix[prefix]);
184
}
185

    
186
const get_remote_storage = make_once(init);
187

    
188
/*
189
 * EXPORTS_START
190
 * EXPORT get_remote_storage
191
 * EXPORTS_END
192
 */
(7-7/9)