Project

General

Profile

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

haketilo / common / storage_client.js @ 2bd35bc4

1
/**
2
 * This file is part of Haketilo.
3
 *
4
 * Function: Storage through messages (client side).
5
 *
6
 * Copyright (C) 2021 Wojtek Kosior
7
 * Redistribution terms are gathered in the `copyright' file.
8
 */
9

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

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

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

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

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

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

    
51
const list_by_prefix = {};
52

    
53
for (const prefix of list_prefixes)
54
    list_by_prefix[prefix] = {prefix, listeners : new Set()};
55

    
56
var resolve_init;
57

    
58
function handle_first_message(message)
59
{
60
    for (let prefix of Object.keys(message))
61
	list_by_prefix[prefix].map = new Map(message[prefix]);
62

    
63
    port.onMessage.removeListener(handle_first_message);
64
    port.onMessage.addListener(handle_message);
65

    
66
    resolve_init();
67
}
68

    
69
function handle_change(change)
70
{
71
    let list = list_by_prefix[change.prefix];
72

    
73
    if (change.new_val === undefined)
74
	list.map.delete(change.item);
75
    else
76
	list.map.set(change.item, change.new_val);
77

    
78
    for (let listener_callback of list.listeners)
79
	listener_callback(change);
80
}
81

    
82
var exports = {};
83

    
84
function start_connection(resolve)
85
{
86
    resolve_init = resolve;
87
    port = browser.runtime.connect({name : CONNECTION_TYPE.REMOTE_STORAGE});
88
    port.onMessage.addListener(handle_first_message);
89
}
90

    
91
async function init() {
92
    await new Promise((resolve, reject) => start_connection(resolve));
93
    return exports;
94
}
95

    
96
for (let call_name of ["set", "remove", "replace", "clear"])
97
    exports [call_name] = (...args) => remote_call(call_name, args);
98

    
99
// TODO: Much of the code below is copy-pasted from /background/storage.mjs.
100
//       This should later be refactored into a separate module
101
//       to avoid duplication.
102

    
103
/*
104
 * Facilitate listening to changes
105
 */
106

    
107
exports.add_change_listener = function (cb, prefixes=list_prefixes)
108
{
109
    if (typeof(prefixes) === "string")
110
	prefixes = [prefixes];
111

    
112
    for (let prefix of prefixes)
113
	list_by_prefix[prefix].listeners.add(cb);
114
}
115

    
116
exports.remove_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.delete(cb);
123
}
124

    
125
/* Prepare some hepler functions to get elements of a list */
126

    
127
function list_items_it(list, with_values=false)
128
{
129
    return with_values ? list.map.entries() : list.map.keys();
130
}
131

    
132
function list_entries_it(list)
133
{
134
    return list_items_it(list, true);
135
}
136

    
137
function list_items(list, with_values=false)
138
{
139
    let array = [];
140

    
141
    for (let item of list_items_it(list, with_values))
142
	array.push(item);
143

    
144
    return array;
145
}
146

    
147
function list_entries(list)
148
{
149
    return list_items(list, true);
150
}
151

    
152
exports.get = function (prefix, item)
153
{
154
    return list_by_prefix[prefix].map.get(item);
155
}
156

    
157
exports.get_all_names = function (prefix)
158
{
159
    return list_items(list_by_prefix[prefix]);
160
}
161

    
162
exports.get_all_names_it = function (prefix)
163
{
164
    return list_items_it(list_by_prefix[prefix]);
165
}
166

    
167
exports.get_all = function (prefix)
168
{
169
    return list_entries(list_by_prefix[prefix]);
170
}
171

    
172
exports.get_all_it = function (prefix)
173
{
174
    return list_entries_it(list_by_prefix[prefix]);
175
}
176

    
177
const get_remote_storage = make_once(init);
178

    
179
/*
180
 * EXPORTS_START
181
 * EXPORT get_remote_storage
182
 * EXPORTS_END
183
 */
(13-13/16)